1- from typing import Any , Dict , Generic , List , Union , cast , TypedDict
1+ from typing import Any , Dict , Generic , List , Union , cast , TypedDict , Generator
22import requests
33from typing_extensions import Literal , TypeVar
44from .exceptions import NoContentError , raise_for_code_and_type
5+ import json
56
67RequestVerb = Literal ["get" , "post" , "put" , "patch" , "delete" ]
78
@@ -24,6 +25,7 @@ def __init__(
2425 verb : RequestVerb ,
2526 headers : Dict [str , str ] = {"Content-Type" : "application/json" },
2627 data : Union [bytes , None ] = None ,
28+ stream : Union [bool , None ] = False ,
2729 ):
2830 self .path = path
2931 self .params = params
@@ -33,6 +35,7 @@ def __init__(
3335 self .data = data
3436 self .headers = headers
3537 self .disable_request_logging = config .get ("disable_request_logging" )
38+ self .stream = stream
3639
3740 def perform (self ) -> Union [T , None ]:
3841 """Is the main function that makes the HTTP request
@@ -152,6 +155,67 @@ def __get_headers(self) -> Dict[Any, Any]:
152155
153156 return _headers
154157
158+ def perform_streaming (self ) -> Generator [Union [T , str ], None , None ]:
159+ """Is the main function that makes the HTTP request
160+ to the JigsawStack API. It uses the path, params, and verb attributes
161+ to make the request.
162+
163+ Returns:
164+ Generator[bytes, None, None]: A generator of bytes
165+
166+ Raises:
167+ requests.HTTPError: If the request fails
168+ """
169+ resp = self .make_request (url = f"{ self .api_url } { self .path } " )
170+
171+ # delete calls do not return a body
172+ if resp .text == "" :
173+ return None
174+
175+ if resp .status_code != 200 :
176+ error = resp .json ()
177+ raise_for_code_and_type (
178+ code = resp .status_code ,
179+ message = error .get ("message" ),
180+ err = error .get ("error" ),
181+ )
182+
183+ def try_parse_data (chunk : bytes ) -> Union [T , str ]:
184+ if not chunk :
185+ return chunk
186+ # Decode bytes to text
187+ text = chunk .decode ("utf-8" )
188+
189+ try :
190+ # Try to parse as JSON
191+ return json .loads (text )
192+ except json .JSONDecodeError :
193+ # Return as text if not valid JSON
194+ return text
195+
196+ # Yield content in chunks
197+ def chunk_generator ():
198+ for chunk in resp .iter_content (chunk_size = 1024 ): # 1KB chunks
199+ if chunk : # Filter out keep-alive new chunks
200+ yield try_parse_data (chunk )
201+
202+ return chunk_generator ()
203+
204+ def perform_with_content_streaming (self ) -> Generator [Union [T , str ], None , None ]:
205+ """
206+ Perform an HTTP request and return the response content as a streaming response.
207+
208+ Returns:
209+ T: The content of the response
210+
211+ Raises:
212+ NoContentError: If the response content is `None`.
213+ """
214+ resp = self .perform_streaming ()
215+ if resp is None :
216+ raise NoContentError ()
217+ return resp
218+
155219 def make_request (self , url : str ) -> requests .Response :
156220 """make_request is a helper function that makes the actual
157221 HTTP request to the JigsawStack API.
@@ -183,6 +247,7 @@ def make_request(self, url: str) -> requests.Response:
183247 json = params ,
184248 headers = headers ,
185249 data = data ,
250+ stream = self .stream ,
186251 )
187252 except requests .HTTPError as e :
188253 raise e
0 commit comments