3434
3535import sys
3636
37- import requests
37+ import httpx
3838
39+ if sys .version_info >= (3 , 11 ):
40+ import typing
41+ else :
42+ import typing_extensions as typing
3943from typesense .configuration import Configuration , Node
4044from typesense .exceptions import (
4145 HTTPStatus0Error ,
4448 TypesenseClientError ,
4549)
4650from typesense .node_manager import NodeManager
47- from typesense .request_handler import RequestHandler , SessionFunctionKwargs
51+ from typesense .request_handler import (
52+ RequestHandler ,
53+ )
54+
55+ TEntityDict = typing .TypeVar ("TEntityDict" )
56+ TParams = typing .TypeVar ("TParams" , bound = typing .Dict [str , typing .Any ])
57+ TBody = typing .TypeVar (
58+ "TBody" , bound = typing .Union [str , bytes , typing .Mapping [str , typing .Any ]]
59+ )
60+
61+
62+ class SessionFunctionKwargs (typing .Generic [TParams , TBody ], typing .TypedDict ):
63+ """
64+ Type definition for keyword arguments used in request functions.
65+
66+ This is an internal abstraction that gets converted to httpx's request parameters.
67+ The `data` field is converted to `content` when passed to httpx.
68+
69+ Note: `verify` and `timeout` are set on the httpx client, not in request kwargs.
70+ However, we include them here for compatibility with the existing API.
71+
72+ Attributes:
73+ params (Optional[Union[TParams, None]]): Query parameters for the request.
74+ Passed as `params` to httpx.
75+
76+ data (Optional[Union[TBody, str, None]]): Body of the request.
77+ Converted to `content` (JSON string) when passed to httpx.
78+
79+ headers (Optional[Dict[str, str]]): Headers for the request.
80+ Passed as `headers` to httpx.
81+
82+ timeout (float): Timeout for the request in seconds.
83+ Set on the httpx client, not in request kwargs.
84+
85+ verify (bool): Whether to verify SSL certificates.
86+ Set on the httpx client, not in request kwargs.
87+ """
88+
89+ params : typing .NotRequired [typing .Union [TParams , None ]]
90+ data : typing .NotRequired [
91+ typing .Union [TBody , str , typing .Dict [str , typing .Any ], None ]
92+ ]
93+ content : typing .NotRequired [typing .Union [TBody , str , None ]]
94+ headers : typing .NotRequired [typing .Dict [str , str ]]
95+ timeout : typing .NotRequired [float ]
96+
4897
4998if sys .version_info >= (3 , 11 ):
5099 import typing
51100else :
52101 import typing_extensions as typing
53102
54- session = requests .sessions .Session ()
55- TParams = typing .TypeVar ("TParams" )
56- TBody = typing .TypeVar ("TBody" )
57- TEntityDict = typing .TypeVar ("TEntityDict" )
103+
104+ class ApiCallProtocol (typing .Protocol ):
105+ """
106+ Protocol defining the interface for API call classes.
107+
108+ This protocol ensures that both sync (ApiCall) and async (AsyncApiCall)
109+ implementations provide the same interface, allowing resource classes
110+ to work with either implementation.
111+ """
112+
113+ config : Configuration
114+ node_manager : NodeManager
115+ request_handler : RequestHandler
116+
117+ def get (
118+ self ,
119+ endpoint : str ,
120+ entity_type : typing .Type [TEntityDict ],
121+ as_json : typing .Union [typing .Literal [True ], typing .Literal [False ]] = True ,
122+ params : typing .Union [TParams , None ] = None ,
123+ ) -> typing .Union [TEntityDict , str ]:
124+ """Execute a GET request to the Typesense API."""
125+ ...
126+
127+ def post (
128+ self ,
129+ endpoint : str ,
130+ entity_type : typing .Type [TEntityDict ],
131+ as_json : typing .Union [typing .Literal [True ], typing .Literal [False ]] = True ,
132+ params : typing .Union [TParams , None ] = None ,
133+ body : typing .Union [TBody , None ] = None ,
134+ ) -> typing .Union [str , TEntityDict ]:
135+ """Execute a POST request to the Typesense API."""
136+ ...
137+
138+ def put (
139+ self ,
140+ endpoint : str ,
141+ entity_type : typing .Type [TEntityDict ],
142+ body : TBody ,
143+ params : typing .Union [TParams , None ] = None ,
144+ ) -> TEntityDict :
145+ """Execute a PUT request to the Typesense API."""
146+ ...
147+
148+ def patch (
149+ self ,
150+ endpoint : str ,
151+ entity_type : typing .Type [TEntityDict ],
152+ body : TBody ,
153+ params : typing .Union [TParams , None ] = None ,
154+ ) -> TEntityDict :
155+ """Execute a PATCH request to the Typesense API."""
156+ ...
157+
158+ def delete (
159+ self ,
160+ endpoint : str ,
161+ entity_type : typing .Type [TEntityDict ],
162+ params : typing .Union [TParams , None ] = None ,
163+ ) -> TEntityDict :
164+ """Execute a DELETE request to the Typesense API."""
165+ ...
58166
59167
60168_SERVER_ERRORS : typing .Final [
61169 typing .Tuple [
62- typing .Type [requests .exceptions .Timeout ],
63- typing .Type [requests .exceptions .ConnectionError ],
64- typing .Type [requests .exceptions .HTTPError ],
65- typing .Type [requests .exceptions .RequestException ],
66- typing .Type [requests .exceptions .SSLError ],
170+ typing .Type [httpx .TimeoutException ],
171+ typing .Type [httpx .ConnectError ],
172+ typing .Type [httpx .HTTPError ],
173+ typing .Type [httpx .RequestError ],
67174 typing .Type [HTTPStatus0Error ],
68175 typing .Type [ServerError ],
69176 typing .Type [ServiceUnavailable ],
70177 ]
71178] = (
72- requests .exceptions .Timeout ,
73- requests .exceptions .ConnectionError ,
74- requests .exceptions .HTTPError ,
75- requests .exceptions .RequestException ,
76- requests .exceptions .SSLError ,
179+ httpx .TimeoutException ,
180+ httpx .ConnectError ,
181+ httpx .HTTPError ,
182+ httpx .RequestError ,
77183 HTTPStatus0Error ,
78184 ServerError ,
79185 ServiceUnavailable ,
@@ -103,6 +209,10 @@ def __init__(self, config: Configuration):
103209 self .config = config
104210 self .node_manager = NodeManager (config )
105211 self .request_handler = RequestHandler (config )
212+ self ._client = httpx .Client (
213+ timeout = config .connection_timeout_seconds ,
214+ verify = config .verify ,
215+ )
106216
107217 @typing .overload
108218 def get (
@@ -166,7 +276,7 @@ def get(
166276 Union[TEntityDict, str]: The response, either as a JSON object or a string.
167277 """
168278 return self ._execute_request (
169- session . get ,
279+ "GET" ,
170280 endpoint ,
171281 entity_type ,
172282 as_json ,
@@ -238,7 +348,7 @@ def post(
238348 Union[TEntityDict, str]: The response, either as a JSON object or a string.
239349 """
240350 return self ._execute_request (
241- session . post ,
351+ "POST" ,
242352 endpoint ,
243353 entity_type ,
244354 as_json ,
@@ -265,7 +375,7 @@ def put(
265375 EntityDict: The response, as a JSON object.
266376 """
267377 return self ._execute_request (
268- session . put ,
378+ "PUT" ,
269379 endpoint ,
270380 entity_type ,
271381 as_json = True ,
@@ -292,7 +402,7 @@ def patch(
292402 EntityDict: The response, as a JSON object.
293403 """
294404 return self ._execute_request (
295- session . patch ,
405+ "PATCH" ,
296406 endpoint ,
297407 entity_type ,
298408 as_json = True ,
@@ -318,7 +428,7 @@ def delete(
318428 EntityDict: The response, as a JSON object.
319429 """
320430 return self ._execute_request (
321- session . delete ,
431+ "DELETE" ,
322432 endpoint ,
323433 entity_type ,
324434 as_json = True ,
@@ -328,13 +438,13 @@ def delete(
328438 @typing .overload
329439 def _execute_request (
330440 self ,
331- fn : typing . Callable [..., requests . models . Response ] ,
441+ method : str ,
332442 endpoint : str ,
333443 entity_type : typing .Type [TEntityDict ],
334444 as_json : typing .Literal [True ],
335445 last_exception : typing .Union [None , Exception ] = None ,
336446 num_retries : int = 0 ,
337- ** kwargs : SessionFunctionKwargs [TParams , TBody ],
447+ ** kwargs : typing . Unpack [ SessionFunctionKwargs [TParams , TBody ] ],
338448 ) -> TEntityDict :
339449 """
340450 Execute a request to the Typesense API with retry logic.
@@ -367,13 +477,13 @@ def _execute_request(
367477 @typing .overload
368478 def _execute_request (
369479 self ,
370- fn : typing . Callable [..., requests . models . Response ] ,
480+ method : str ,
371481 endpoint : str ,
372482 entity_type : typing .Type [TEntityDict ],
373483 as_json : typing .Literal [False ],
374484 last_exception : typing .Union [None , Exception ] = None ,
375485 num_retries : int = 0 ,
376- ** kwargs : SessionFunctionKwargs [TParams , TBody ],
486+ ** kwargs : typing . Unpack [ SessionFunctionKwargs [TParams , TBody ] ],
377487 ) -> str :
378488 """
379489 Execute a request to the Typesense API with retry logic.
@@ -405,13 +515,13 @@ def _execute_request(
405515
406516 def _execute_request (
407517 self ,
408- fn : typing . Callable [..., requests . models . Response ] ,
518+ method : str ,
409519 endpoint : str ,
410520 entity_type : typing .Type [TEntityDict ],
411521 as_json : typing .Union [typing .Literal [True ], typing .Literal [False ]] = True ,
412522 last_exception : typing .Union [None , Exception ] = None ,
413523 num_retries : int = 0 ,
414- ** kwargs : SessionFunctionKwargs [TParams , TBody ],
524+ ** kwargs : typing . Unpack [ SessionFunctionKwargs [TParams , TBody ] ],
415525 ) -> typing .Union [TEntityDict , str ]:
416526 """
417527 Execute a request to the Typesense API with retry logic.
@@ -420,7 +530,7 @@ def _execute_request(
420530 node selection, error handling, and retries.
421531
422532 Args:
423- fn (Callable ): The HTTP method function to use (e.g., session.get ).
533+ method (str ): The HTTP method to use (e.g., "GET", "POST" ).
424534
425535 endpoint (str): The API endpoint to call.
426536
@@ -449,7 +559,7 @@ def _execute_request(
449559
450560 try :
451561 return self ._make_request_and_process_response (
452- fn ,
562+ method ,
453563 url ,
454564 entity_type ,
455565 as_json ,
@@ -458,7 +568,7 @@ def _execute_request(
458568 except _SERVER_ERRORS as server_error :
459569 self .node_manager .set_node_health (node , is_healthy = False )
460570 return self ._execute_request (
461- fn ,
571+ method ,
462572 endpoint ,
463573 entity_type ,
464574 as_json ,
@@ -469,18 +579,19 @@ def _execute_request(
469579
470580 def _make_request_and_process_response (
471581 self ,
472- fn : typing . Callable [..., requests . models . Response ] ,
582+ method : str ,
473583 url : str ,
474584 entity_type : typing .Type [TEntityDict ],
475585 as_json : bool ,
476- ** kwargs : SessionFunctionKwargs [TParams , TBody ],
586+ ** kwargs : typing . Unpack [ SessionFunctionKwargs [TParams , TBody ] ],
477587 ) -> typing .Union [TEntityDict , str ]:
478588 """Make the API request and process the response."""
479589 request_response = self .request_handler .make_request (
480- fn = fn ,
590+ method = method ,
481591 url = url ,
482592 as_json = as_json ,
483593 entity_type = entity_type ,
594+ client = self ._client ,
484595 ** kwargs ,
485596 )
486597 self .node_manager .set_node_health (self .node_manager .get_node (), is_healthy = True )
@@ -493,12 +604,23 @@ def _make_request_and_process_response(
493604 def _prepare_request_params (
494605 self ,
495606 endpoint : str ,
496- ** kwargs : SessionFunctionKwargs [TParams , TBody ],
607+ ** kwargs : typing . Unpack [ SessionFunctionKwargs [TParams , TBody ] ],
497608 ) -> typing .Tuple [Node , str , SessionFunctionKwargs [TParams , TBody ]]:
609+ """
610+ Prepare request parameters including node selection and URL construction.
611+
612+ Args:
613+ endpoint: The API endpoint path.
614+ **kwargs: Request parameters following SessionFunctionKwargs structure.
615+
616+ Returns:
617+ Tuple of (node, full_url, kwargs_dict) where kwargs_dict contains
618+ the request parameters as a regular dict for further processing.
619+ """
498620 node = self .node_manager .get_node ()
499621 url = node .url () + endpoint
500622
501- if kwargs .get ("params" ):
502- self .request_handler .normalize_params (kwargs [ " params" ] )
623+ if params := kwargs .get ("params" ):
624+ self .request_handler .normalize_params (params )
503625
504626 return node , url , kwargs
0 commit comments