Skip to content

Commit 5840ac3

Browse files
committed
fix: resolve typesense imports in mypy and fix the type errors it surfaces
1 parent 9487502 commit 5840ac3

25 files changed

Lines changed: 255 additions & 152 deletions

‎setup.cfg‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ enable_error_code =
5656
redundant-self,
5757

5858
explicit_package_bases = true
59+
mypy_path = src
5960
ignore_missing_imports = true
6061
strict = true
6162
warn_unreachable = true

‎src/typesense/async_/analytics_rule_v1.py‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -74,11 +74,9 @@ async def retrieve(
7474
Union[RuleSchemaForQueries, RuleSchemaForCounters]:
7575
The schema containing the rule details.
7676
"""
77-
response: typing.Union[
78-
RuleSchemaForQueries, RuleSchemaForCounters
79-
] = await self.api_call.get(
77+
response = await self.api_call.get(
8078
self._endpoint_path,
81-
entity_type=dict,
79+
entity_type=typing.Dict[str, typing.Any],
8280
as_json=True,
8381
)
8482
return typing.cast(
@@ -101,7 +99,7 @@ async def delete(self) -> RuleDeleteSchema:
10199
return response
102100

103101
@property
104-
@warn_deprecation( # type: ignore[untyped-decorator]
102+
@warn_deprecation(
105103
"AsyncAnalyticsRuleV1 is deprecated on v30+. Use client.analytics.rules[rule_id] instead.",
106104
flag_name="analytics_rules_v1_deprecation",
107105
)

‎src/typesense/async_/analytics_rules_v1.py‎

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ def __getitem__(self, rule_id: str) -> AsyncAnalyticsRuleV1:
8989
self.rules[rule_id] = AsyncAnalyticsRuleV1(self.api_call, rule_id)
9090
return self.rules[rule_id]
9191

92-
@warn_deprecation( # type: ignore[untyped-decorator]
92+
@warn_deprecation(
9393
"AsyncAnalyticsRulesV1 is deprecated on v30+. Use client.analytics instead.",
9494
flag_name="analytics_rules_v1_deprecation",
9595
)
@@ -115,21 +115,19 @@ async def create(
115115
The created rule. Returns RuleSchemaForCounters for counter rules
116116
and RuleSchemaForQueries for query rules.
117117
"""
118-
response: typing.Union[
119-
RuleSchemaForCounters, RuleSchemaForQueries
120-
] = await self.api_call.post(
118+
response = await self.api_call.post(
121119
AsyncAnalyticsRulesV1.resource_path,
122120
body=rule,
123121
params=rule_parameters,
124122
as_json=True,
125-
entity_type=dict,
123+
entity_type=typing.Dict[str, typing.Any],
126124
)
127125
return typing.cast(
128126
typing.Union[RuleSchemaForCounters, RuleSchemaForQueries],
129127
response,
130128
)
131129

132-
@warn_deprecation( # type: ignore[untyped-decorator]
130+
@warn_deprecation(
133131
"AsyncAnalyticsRulesV1 is deprecated on v30+. Use client.analytics instead.",
134132
flag_name="analytics_rules_v1_deprecation",
135133
)
@@ -148,19 +146,17 @@ async def upsert(
148146
Returns:
149147
Union[RuleSchemaForCounters, RuleCreateSchemaForQueries]: The upserted rule.
150148
"""
151-
response: typing.Union[
152-
RuleSchemaForCounters, RuleCreateSchemaForQueries
153-
] = await self.api_call.put(
149+
response = await self.api_call.put(
154150
"/".join([self.resource_path, rule_id]),
155151
body=rule,
156-
entity_type=dict,
152+
entity_type=typing.Dict[str, typing.Any],
157153
)
158154
return typing.cast(
159155
typing.Union[RuleSchemaForCounters, RuleCreateSchemaForQueries],
160156
response,
161157
)
162158

163-
@warn_deprecation( # type: ignore[untyped-decorator]
159+
@warn_deprecation(
164160
"AsyncAnalyticsRulesV1 is deprecated on v30+. Use client.analytics instead.",
165161
flag_name="analytics_rules_v1_deprecation",
166162
)

‎src/typesense/async_/api_call.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
import typing_extensions as typing
6161

6262
TEntityDict = typing.TypeVar("TEntityDict")
63-
TParams = typing.TypeVar("TParams", bound=typing.Dict[str, typing.Any])
63+
TParams = typing.TypeVar("TParams", bound=typing.Mapping[str, object])
6464
TBody = typing.TypeVar(
6565
"TBody", bound=typing.Union[str, bytes, typing.Mapping[str, typing.Any]]
6666
)
@@ -95,7 +95,7 @@ class SessionFunctionKwargs(typing.Generic[TParams, TBody], typing.TypedDict):
9595

9696
params: typing.NotRequired[typing.Union[TParams, None]]
9797
data: typing.NotRequired[typing.Union[TBody, None]]
98-
content: typing.NotRequired[typing.Union[TBody, str, None]]
98+
content: typing.NotRequired[typing.Union[str, bytes, None]]
9999
headers: typing.NotRequired[typing.Dict[str, str]]
100100
timeout: typing.NotRequired[float]
101101

‎src/typesense/async_/client.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -164,5 +164,5 @@ def typed_collection(
164164
"""
165165
if name is None:
166166
name = model.__name__.lower()
167-
collection: AsyncCollection[TDoc] = self.collections[name]
168-
return collection
167+
# ``collections`` is typed for the default DocumentSchema; narrow it to the model.
168+
return typing.cast(AsyncCollection[TDoc], self.collections[name])

‎src/typesense/async_/documents.py‎

Lines changed: 50 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,17 @@
6161
None,
6262
]
6363

64+
# One line of an import response. ``ImportResponse`` is a union of lists, one per
65+
# return mode, so the helpers below build a list of these and ``import_`` casts it
66+
# to the list type its overloads promise.
67+
_ImportResponseItem = typing.Union[
68+
ImportResponseWithDoc[TDoc],
69+
ImportResponseWithId,
70+
ImportResponseWithDocAndId[TDoc],
71+
ImportResponseSuccess,
72+
ImportResponseFail[TDoc],
73+
]
74+
6475

6576
class AsyncDocuments(typing.Generic[TDoc]):
6677
"""
@@ -125,12 +136,14 @@ async def create(
125136
Returns:
126137
TDoc: The created document.
127138
"""
128-
dirty_values_parameters = dirty_values_parameters or {}
129-
dirty_values_parameters["action"] = "create"
139+
write_parameters: typing.Dict[str, object] = {
140+
**(dirty_values_parameters or {}),
141+
"action": "create",
142+
}
130143
response = await self.api_call.post(
131144
self._endpoint_path(),
132145
body=document,
133-
params=dirty_values_parameters,
146+
params=write_parameters,
134147
as_json=True,
135148
entity_type=typing.Dict[str, str],
136149
)
@@ -154,7 +167,14 @@ async def create_many(
154167
The list of import responses.
155168
"""
156169
logger.warn("`create_many` is deprecated: please use `import_`.")
157-
return await self.import_(documents, dirty_values_parameters)
170+
# Dirty values parameters are a subset of the write parameters.
171+
return await self.import_(
172+
documents,
173+
typing.cast(
174+
typing.Optional[DocumentWriteParameters],
175+
dirty_values_parameters,
176+
),
177+
)
158178

159179
async def upsert(
160180
self,
@@ -172,12 +192,14 @@ async def upsert(
172192
Returns:
173193
TDoc: The upserted document.
174194
"""
175-
dirty_values_parameters = dirty_values_parameters or {}
176-
dirty_values_parameters["action"] = "upsert"
195+
write_parameters: typing.Dict[str, object] = {
196+
**(dirty_values_parameters or {}),
197+
"action": "upsert",
198+
}
177199
response = await self.api_call.post(
178200
self._endpoint_path(),
179201
body=document,
180-
params=dirty_values_parameters,
202+
params=write_parameters,
181203
as_json=True,
182204
entity_type=typing.Dict[str, str],
183205
)
@@ -199,12 +221,14 @@ async def update(
199221
Returns:
200222
UpdateByFilterResponse: The response containing information about the update.
201223
"""
202-
dirty_values_parameters = dirty_values_parameters or {}
203-
dirty_values_parameters["action"] = "update"
224+
update_parameters: typing.Dict[str, object] = {
225+
**(dirty_values_parameters or {}),
226+
"action": "update",
227+
}
204228
response: UpdateByFilterResponse = await self.api_call.patch(
205229
self._endpoint_path(),
206230
body=document,
207-
params=dirty_values_parameters,
231+
params=update_parameters,
208232
entity_type=UpdateByFilterResponse,
209233
)
210234
return response
@@ -301,9 +325,14 @@ async def import_(
301325
return await self._import_raw(documents, import_parameters)
302326

303327
if batch_size:
304-
return await self._batch_import(documents, import_parameters, batch_size)
305-
306-
return await self._bulk_import(documents, import_parameters)
328+
response_objs = await self._batch_import(
329+
documents,
330+
import_parameters,
331+
batch_size,
332+
)
333+
else:
334+
response_objs = await self._bulk_import(documents, import_parameters)
335+
return typing.cast(ImportResponse[TDoc], response_objs)
307336

308337
async def export(
309338
self,
@@ -410,9 +439,9 @@ async def _batch_import(
410439
documents: typing.List[TDoc],
411440
import_parameters: _ImportParameters,
412441
batch_size: int,
413-
) -> ImportResponse[TDoc]:
442+
) -> typing.List[_ImportResponseItem[TDoc]]:
414443
"""Import documents in batches."""
415-
response_objs: ImportResponse[TDoc] = []
444+
response_objs: typing.List[_ImportResponseItem[TDoc]] = []
416445
for batch_index in range(0, len(documents), batch_size):
417446
batch = documents[batch_index : batch_index + batch_size]
418447
api_response = await self._bulk_import(batch, import_parameters)
@@ -423,7 +452,7 @@ async def _bulk_import(
423452
self,
424453
documents: typing.List[TDoc],
425454
import_parameters: _ImportParameters,
426-
) -> ImportResponse[TDoc]:
455+
) -> typing.List[_ImportResponseItem[TDoc]]:
427456
"""Import a list of documents in bulk."""
428457
document_strs = [json.dumps(doc) for doc in documents]
429458
if not document_strs:
@@ -439,9 +468,12 @@ async def _bulk_import(
439468
)
440469
return self._parse_import_response(res)
441470

442-
def _parse_import_response(self, response: str) -> ImportResponse[TDoc]:
471+
def _parse_import_response(
472+
self,
473+
response: str,
474+
) -> typing.List[_ImportResponseItem[TDoc]]:
443475
"""Parse the import response string into a list of response objects."""
444-
response_objs: typing.List[ImportResponse] = []
476+
response_objs: typing.List[_ImportResponseItem[TDoc]] = []
445477
for res_obj_str in response.split("\n"):
446478
try:
447479
res_obj_json = json.loads(res_obj_str)

‎src/typesense/async_/keys.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,11 +103,11 @@ async def create(self, schema: ApiKeyCreateSchema) -> ApiKeyCreateResponseSchema
103103
... }
104104
... )
105105
"""
106-
response: ApiKeySchema = await self.api_call.post(
106+
response: ApiKeyCreateResponseSchema = await self.api_call.post(
107107
AsyncKeys.resource_path,
108108
as_json=True,
109109
body=schema,
110-
entity_type=ApiKeySchema,
110+
entity_type=ApiKeyCreateResponseSchema,
111111
)
112112
return response
113113

‎src/typesense/async_/operations.py‎

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,10 @@ def __init__(self, api_call: AsyncApiCall):
6060
"""
6161
self.api_call = api_call
6262

63+
# The generic ``str`` overload below also matches "schema_changes"; overloads are
64+
# tried in order, so this one wins.
6365
@typing.overload
64-
async def perform(
66+
async def perform( # type: ignore[overload-overlap]
6567
self,
6668
operation_name: typing.Literal["schema_changes"],
6769
query_params: None = None,
@@ -132,36 +134,36 @@ async def perform(
132134
@typing.overload
133135
async def perform(
134136
self,
135-
operation_name: str,
136-
query_params: typing.Union[typing.Dict[str, str], None] = None,
137+
operation_name: typing.Literal["snapshot"],
138+
query_params: SnapshotParameters,
137139
) -> OperationResponse:
138140
"""
139-
Perform a generic operation.
141+
Perform a snapshot operation.
140142
141143
Args:
142-
operation_name (str): The name of the operation.
143-
query_params (Union[Dict[str, str], None], optional):
144-
Query parameters for the operation.
144+
operation_name (Literal["snapshot"]): The name of the operation.
145+
query_params (SnapshotParameters): Query parameters for the snapshot operation.
145146
146147
Returns:
147-
OperationResponse: The response from the operation.
148+
OperationResponse: The response from the snapshot operation.
148149
"""
149150

150151
@typing.overload
151152
async def perform(
152153
self,
153-
operation_name: typing.Literal["snapshot"],
154-
query_params: SnapshotParameters,
154+
operation_name: str,
155+
query_params: typing.Union[typing.Dict[str, str], None] = None,
155156
) -> OperationResponse:
156157
"""
157-
Perform a snapshot operation.
158+
Perform a generic operation.
158159
159160
Args:
160-
operation_name (Literal["snapshot"]): The name of the operation.
161-
query_params (SnapshotParameters): Query parameters for the snapshot operation.
161+
operation_name (str): The name of the operation.
162+
query_params (Union[Dict[str, str], None], optional):
163+
Query parameters for the operation.
162164
163165
Returns:
164-
OperationResponse: The response from the snapshot operation.
166+
OperationResponse: The response from the operation.
165167
"""
166168

167169
async def perform(
@@ -181,7 +183,7 @@ async def perform(
181183
typing.Dict[str, str],
182184
None,
183185
] = None,
184-
) -> OperationResponse:
186+
) -> typing.Union[OperationResponse, typing.List[SchemaChangesResponse]]:
185187
"""
186188
Perform an operation on the Typesense API.
187189
@@ -202,13 +204,16 @@ async def perform(
202204
>>> response = await operations.perform("vote")
203205
>>> health = await operations.is_healthy()
204206
"""
205-
response: OperationResponse = await self.api_call.post(
207+
response = await self.api_call.post(
206208
self._endpoint_path(operation_name),
207209
params=query_params,
208210
as_json=True,
209-
entity_type=OperationResponse,
211+
entity_type=object,
212+
)
213+
return typing.cast(
214+
typing.Union[OperationResponse, typing.List[SchemaChangesResponse]],
215+
response,
210216
)
211-
return response
212217

213218
async def is_healthy(self) -> bool:
214219
"""
@@ -222,16 +227,14 @@ async def is_healthy(self) -> bool:
222227
>>> healthy = await operations.is_healthy()
223228
>>> print(healthy)
224229
"""
225-
call_resp: HealthCheckResponse = await self.api_call.get(
230+
call_resp: object = await self.api_call.get(
226231
AsyncOperations.health_path,
227232
as_json=True,
228233
entity_type=HealthCheckResponse,
229234
)
230-
if isinstance(call_resp, typing.Dict):
231-
is_ok: bool = call_resp.get("ok", False)
232-
else:
233-
is_ok = False
234-
return is_ok
235+
if isinstance(call_resp, dict):
236+
return bool(call_resp.get("ok", False))
237+
return False
235238

236239
async def toggle_slow_request_log(
237240
self,

‎src/typesense/async_/override.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ async def delete(self) -> OverrideDeleteSchema:
8787
return response
8888

8989
@property
90-
@warn_deprecation( # type: ignore[untyped-decorator]
90+
@warn_deprecation(
9191
"The override API (collections/{collection}/overrides/{override_id}) is deprecated is removed on v30+. "
9292
"Use curation sets (curation_sets) instead.",
9393
flag_name="overrides_deprecation",

‎src/typesense/async_/overrides.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ async def retrieve(self) -> OverrideRetrieveSchema:
129129
)
130130
return response
131131

132-
@warn_deprecation( # type: ignore[untyped-decorator]
132+
@warn_deprecation(
133133
"AsyncOverrides is deprecated on v30+. Use client.curation_sets instead.",
134134
flag_name="overrides_deprecation",
135135
)

0 commit comments

Comments
 (0)