1+ import inspect
12from dataclasses import dataclass , field
2- from typing import Any , Dict , List , Optional
3+ from typing import Any , Callable , Dict , List , Optional
34
45import httpx
6+ from httpx ._models import Request , Response
7+ from httpx ._types import QueryParamTypes , RequestContent , RequestData , RequestFiles
58
69from common .http .clientToken import Token , resolve_token
710from common .http .interceptor import Interceptor , InterceptorRequestContext , InterceptorResponseContext
811from common .logging import ConsoleLogger , Logger
912
13+ console_logger = ConsoleLogger ()
14+
1015
1116@dataclass (frozen = True )
1217class ClientOptions :
@@ -23,9 +28,9 @@ class ClientOptions:
2328 """
2429
2530 base_url : Optional [str ] = None
26- headers : Optional [ Dict [str , str ] ] = field (default_factory = dict )
31+ headers : Dict [str , str ] = field (default_factory = dict )
2732 timeout : Optional [float ] = None
28- logger : Logger = field (default_factory = ConsoleLogger )
33+ logger : Logger = field (default_factory = lambda : console_logger . create_logger ( "http-client" ) )
2934 token : Optional [Token ] = None
3035 interceptors : Optional [List [Interceptor ]] = field (default_factory = list )
3136
@@ -53,7 +58,7 @@ def __init__(self, options: ClientOptions):
5358 self ._interceptors = list (options .interceptors or [])
5459
5560 self .http = httpx .AsyncClient (
56- base_url = options .base_url ,
61+ base_url = httpx . URL ( options .base_url ) if options . base_url else "" ,
5762 headers = options .headers ,
5863 timeout = options .timeout ,
5964 )
@@ -77,31 +82,42 @@ async def _prepare_headers(self, headers: Optional[Dict[str, str]], token: Optio
7782 return req_headers
7883
7984 async def get (
80- self , url : str , * , headers : Optional [Dict [str , str ]] = None , token : Optional [Token ] = None , ** kwargs
85+ self ,
86+ url : str ,
87+ * ,
88+ headers : Optional [Dict [str , str ]] = None ,
89+ token : Optional [Token ] = None ,
90+ params : Optional [QueryParamTypes ] = None ,
91+ ** kwargs : Any ,
8192 ) -> httpx .Response :
8293 """
8394 Send a GET request.
8495
8596 Args:
8697 url: The URL path or full URL.
8798 headers: Optional per-request headers.
99+ params: Optional query parameters.
88100 token: Optional per-request token (overrides default).
89101 **kwargs: Additional httpx.AsyncClient.get arguments.
90102
91103 Returns:
92104 httpx.Response
93105 """
94106 req_headers = await self ._prepare_headers (headers , token )
95- return await self .http .get (url , headers = req_headers , ** kwargs )
107+ return await self .http .get (url , headers = req_headers , params = params , ** kwargs )
96108
97109 async def post (
98110 self ,
99111 url : str ,
100- data : Any = None ,
101112 * ,
113+ content : Optional [RequestContent ] = None ,
114+ data : Optional [RequestData ] = None ,
115+ files : Optional [RequestFiles ] = None ,
116+ json : Optional [Any ] = None ,
117+ params : Optional [QueryParamTypes ] = None ,
102118 headers : Optional [Dict [str , str ]] = None ,
103119 token : Optional [Token ] = None ,
104- ** kwargs ,
120+ ** kwargs : Any ,
105121 ) -> httpx .Response :
106122 """
107123 Send a POST request.
@@ -110,23 +126,40 @@ async def post(
110126 url: The URL path or full URL.
111127 data: The request body.
112128 headers: Optional per-request headers.
129+ params: Optional query parameters.
130+ content: The request body.
131+ files: The request files.
132+ json: The request JSON body.
113133 token: Optional per-request token (overrides default).
114134 **kwargs: Additional httpx.AsyncClient.post arguments.
115135
116136 Returns:
117137 httpx.Response
118138 """
119139 req_headers = await self ._prepare_headers (headers , token )
120- return await self .http .post (url , data = data , headers = req_headers , ** kwargs )
140+ return await self .http .post (
141+ url ,
142+ data = data ,
143+ files = files ,
144+ json = json ,
145+ content = content ,
146+ params = params ,
147+ headers = req_headers ,
148+ ** kwargs ,
149+ )
121150
122151 async def put (
123152 self ,
124153 url : str ,
125- data : Any = None ,
126154 * ,
155+ content : Optional [RequestContent ] = None ,
156+ data : Optional [RequestData ] = None ,
157+ files : Optional [RequestFiles ] = None ,
158+ json : Optional [Any ] = None ,
159+ params : Optional [QueryParamTypes ] = None ,
127160 headers : Optional [Dict [str , str ]] = None ,
128161 token : Optional [Token ] = None ,
129- ** kwargs ,
162+ ** kwargs : Any ,
130163 ) -> httpx .Response :
131164 """
132165 Send a PUT request.
@@ -135,23 +168,40 @@ async def put(
135168 url: The URL path or full URL.
136169 data: The request body.
137170 headers: Optional per-request headers.
171+ params: Optional query parameters.
172+ content: The request body.
173+ files: The request files.
174+ json: The request JSON body.
138175 token: Optional per-request token (overrides default).
139176 **kwargs: Additional httpx.AsyncClient.put arguments.
140177
141178 Returns:
142179 httpx.Response
143180 """
144181 req_headers = await self ._prepare_headers (headers , token )
145- return await self .http .put (url , data = data , headers = req_headers , ** kwargs )
182+ return await self .http .put (
183+ url ,
184+ data = data ,
185+ files = files ,
186+ json = json ,
187+ content = content ,
188+ params = params ,
189+ headers = req_headers ,
190+ ** kwargs ,
191+ )
146192
147193 async def patch (
148194 self ,
149195 url : str ,
150- data : Any = None ,
151196 * ,
197+ content : Optional [RequestContent ] = None ,
198+ data : Optional [RequestData ] = None ,
199+ files : Optional [RequestFiles ] = None ,
200+ json : Optional [Any ] = None ,
201+ params : Optional [QueryParamTypes ] = None ,
152202 headers : Optional [Dict [str , str ]] = None ,
153203 token : Optional [Token ] = None ,
154- ** kwargs ,
204+ ** kwargs : Any ,
155205 ) -> httpx .Response :
156206 """
157207 Send a PATCH request.
@@ -160,41 +210,66 @@ async def patch(
160210 url: The URL path or full URL.
161211 data: The request body.
162212 headers: Optional per-request headers.
213+ params: Optional query parameters.
214+ content: The request body.
215+ files: The request files.
216+ json: The request JSON body.
163217 token: Optional per-request token (overrides default).
164218 **kwargs: Additional httpx.AsyncClient.patch arguments.
165219
166220 Returns:
167221 httpx.Response
168222 """
169223 req_headers = await self ._prepare_headers (headers , token )
170- return await self .http .patch (url , data = data , headers = req_headers , ** kwargs )
224+ return await self .http .patch (
225+ url ,
226+ data = data ,
227+ files = files ,
228+ json = json ,
229+ content = content ,
230+ params = params ,
231+ headers = req_headers ,
232+ ** kwargs ,
233+ )
171234
172235 async def delete (
173- self , url : str , * , headers : Optional [Dict [str , str ]] = None , token : Optional [Token ] = None , ** kwargs
236+ self ,
237+ url : str ,
238+ * ,
239+ headers : Optional [Dict [str , str ]] = None ,
240+ token : Optional [Token ] = None ,
241+ params : Optional [QueryParamTypes ] = None ,
242+ ** kwargs : Any ,
174243 ) -> httpx .Response :
175244 """
176245 Send a DELETE request.
177246
178247 Args:
179248 url: The URL path or full URL.
180249 headers: Optional per-request headers.
250+ params: Optional query parameters.
181251 token: Optional per-request token (overrides default).
182252 **kwargs: Additional httpx.AsyncClient.delete arguments.
183253
184254 Returns:
185255 httpx.Response
186256 """
187257 req_headers = await self ._prepare_headers (headers , token )
188- return await self .http .delete (url , headers = req_headers , ** kwargs )
258+ return await self .http .delete (url , headers = req_headers , params = params , ** kwargs )
189259
190260 async def request (
191261 self ,
192262 method : str ,
193263 url : str ,
194264 * ,
265+ params : Optional [QueryParamTypes ] = None ,
195266 headers : Optional [Dict [str , str ]] = None ,
196267 token : Optional [Token ] = None ,
197- ** kwargs ,
268+ content : Optional [RequestContent ] = None ,
269+ data : Optional [RequestData ] = None ,
270+ files : Optional [RequestFiles ] = None ,
271+ json : Optional [Any ] = None ,
272+ ** kwargs : Any ,
198273 ) -> httpx .Response :
199274 """
200275 Send a custom HTTP request.
@@ -203,14 +278,29 @@ async def request(
203278 method: HTTP method (GET, POST, etc).
204279 url: The URL path or full URL.
205280 headers: Optional per-request headers.
281+ params: Optional query parameters.
282+ content: The request body.
283+ data: The request body.
284+ files: The request files.
285+ json: The request JSON body.
206286 token: Optional per-request token (overrides default).
207287 **kwargs: Additional httpx.AsyncClient.request arguments.
208288
209289 Returns:
210290 httpx.Response
211291 """
212292 req_headers = await self ._prepare_headers (headers , token )
213- return await self .http .request (method , url , headers = req_headers , ** kwargs )
293+ return await self .http .request (
294+ method ,
295+ url ,
296+ headers = req_headers ,
297+ params = params ,
298+ content = content ,
299+ data = data ,
300+ files = files ,
301+ json = json ,
302+ ** kwargs ,
303+ )
214304
215305 async def _resolve_token (self , token : Optional [Token ]) -> Optional [str ]:
216306 """
@@ -241,55 +331,55 @@ def _update_event_hooks(self) -> None:
241331 """
242332 Internal: Update the httpx.AsyncClient event_hooks to match current interceptors.
243333 """
244- event_hooks_dict = {}
334+ event_hooks_dict : Dict [ str , List [ Callable [[ Any ], Any ]]] = {}
245335 for hook in self ._interceptors :
246336 if hasattr (hook , "request" ):
247337
248- def make_request_wrapper ( h ):
249- async def wrapper (request ):
338+ def _make_request_wrapper ( h : Interceptor ):
339+ async def wrapper (request : Request ):
250340 ctx = InterceptorRequestContext (request , self .logger )
251341 result = h .request (ctx )
252- if hasattr (result , "__await__" ):
342+ if inspect . isawaitable (result ):
253343 return await result
254344 return result
255345
256346 return wrapper
257347
258- event_hooks_dict .setdefault ("request" , []).append (make_request_wrapper (hook ))
348+ event_hooks_dict .setdefault ("request" , []).append (_make_request_wrapper (hook ))
259349 if hasattr (hook , "response" ):
260350
261- def make_response_wrapper ( h ):
262- async def wrapper (response ):
351+ def _make_response_wrapper ( h : Interceptor ):
352+ async def wrapper (response : Response ):
263353 ctx = InterceptorResponseContext (response , self .logger )
264354 result = h .response (ctx )
265- if hasattr (result , "__await__" ):
355+ if inspect . isawaitable (result ):
266356 return await result
267357 return result
268358
269359 return wrapper
270360
271- event_hooks_dict .setdefault ("response" , []).append (make_response_wrapper (hook ))
361+ event_hooks_dict .setdefault ("response" , []).append (_make_response_wrapper (hook ))
272362 self .http .event_hooks = event_hooks_dict
273363
274- def clone (self , ** overrides ) -> "Client" :
364+ def clone (self , overrides : Optional [ ClientOptions ] = None ) -> "Client" :
275365 """
276366 Create a new Client instance with merged configuration.
277367
278368 Args:
279- ** overrides: Partial ClientOptions fields to override.
369+ overrides: Optional ClientOptions object to override fields .
280370
281371 Returns:
282372 A new Client instance with merged options and a cloned interceptor list.
283373 """
284- # Merge options, shallow copy interceptors array
374+ overrides = overrides or ClientOptions ()
285375 merged_options = ClientOptions (
286- base_url = overrides .get ( " base_url" , self .options .base_url ) ,
287- headers = {** self .options .headers , ** overrides .get ( " headers" , {})}
288- if overrides .get ( "headers" )
289- else dict ( self .options .headers ) ,
290- timeout = overrides .get ( "timeout" , self .options .timeout ) ,
291- logger = overrides .get ( "logger" , self . options . logger ),
292- token = overrides .get ( "token" , self . options . token ),
293- interceptors = list (overrides . get ( "interceptors" , self ._interceptors ) ),
376+ base_url = overrides .base_url if overrides . base_url is not None else self .options .base_url ,
377+ headers = {** self .options .headers , ** ( overrides .headers or {})},
378+ timeout = overrides . timeout if overrides .timeout is not None else self . options . timeout ,
379+ logger = overrides . logger if overrides . logger is not None else self .options .logger ,
380+ token = overrides .token if overrides . token is not None else self .options .token ,
381+ interceptors = list ( overrides .interceptors )
382+ if overrides .interceptors is not None
383+ else list (self ._interceptors ),
294384 )
295385 return Client (merged_options )
0 commit comments