1212from trakt .core import TIMEOUT
1313from trakt .errors import (BadRequestException , BadResponseException ,
1414 OAuthException , OAuthRefreshException )
15+ from trakt .utils import build_uri
1516
1617__author__ = 'Elan Ruusamäe'
1718
@@ -39,20 +40,23 @@ def __init__(self, base_url: str, session: Session, timeout=None):
3940 self .session = session
4041 self .timeout = timeout or TIMEOUT
4142
42- def get (self , url : str ):
43+ def get (self , url : str , include_headers = False ):
4344 """
4445 Send a GET request to the specified URL.
4546
4647 Parameters:
4748 url (str): The endpoint URL to send the GET request to.
49+ include_headers (bool): When true, return a ``(response, headers)``
50+ tuple instead of just the decoded JSON body.
4851
4952 Returns:
50- dict: The JSON-decoded response from the server.
53+ dict or tuple: The JSON-decoded response from the server, or a
54+ ``(response, headers)`` tuple when ``include_headers`` is true.
5155
5256 Raises:
5357 Various exceptions from `raise_if_needed` based on HTTP status codes.
5458 """
55- return self .request ('get' , url )
59+ return self .request ('get' , url , include_headers = include_headers )
5660
5761 def delete (self , url : str ):
5862 self .request ('delete' , url )
@@ -76,6 +80,35 @@ def put(self, url: str, data):
7680 """
7781 return self .request ('put' , url , data = data )
7882
83+ def iter_pages (self , url , ** params ):
84+ """Yield successive pages for a paginated GET endpoint."""
85+ page = 1
86+ while True :
87+ page_url = build_uri (url , ** params ) if page == 1 else build_uri (
88+ url , page = page , ** params
89+ )
90+ page_data , headers = self .get (page_url , include_headers = True )
91+ yield page_data
92+ try :
93+ page_count = int (headers .get ('X-Pagination-Page-Count' , 1 ))
94+ except (TypeError , ValueError ):
95+ page_count = 1
96+ if page >= page_count :
97+ break
98+ page += 1
99+
100+ def paginate (self , url , ** params ):
101+ """Return a flattened list from all pages of a paginated GET endpoint."""
102+ results = []
103+ for page_data in self .iter_pages (url , ** params ):
104+ if page_data is None :
105+ continue
106+ if isinstance (page_data , list ):
107+ results .extend (page_data )
108+ else :
109+ results .append (page_data )
110+ return results
111+
79112 @property
80113 def auth (self ):
81114 """
@@ -96,7 +129,7 @@ def auth(self, auth):
96129 """
97130 self ._auth = auth
98131
99- def request (self , method , url , data = None ):
132+ def request (self , method , url , data = None , include_headers = False ):
100133 """
101134 Send an HTTP request to the Trakt API and process the response.
102135
@@ -109,7 +142,9 @@ def request(self, method, url, data=None):
109142 data (dict, optional): Payload to send with the request. Defaults to None.
110143
111144 Returns:
112- dict or None: Decoded JSON response from the Trakt API, or None for 204 No Content responses
145+ dict or tuple or None: Decoded JSON response from the Trakt API,
146+ ``(response, headers)`` when ``include_headers`` is true, or None
147+ for 204 No Content responses.
113148
114149 Raises:
115150 TraktException: If the API returns a non-200 status code
@@ -130,11 +165,16 @@ def request(self, method, url, data=None):
130165 else :
131166 response = self .session .request (method , url , headers = self .headers , auth = self .auth , timeout = self .timeout , data = json .dumps (data ))
132167 self .logger .debug ('RESPONSE [%s] (%s): %s' , method , url , str (response ))
168+ headers = response .headers .copy ()
133169 if response .status_code == 204 : # HTTP no content
134- return None
135- self .raise_if_needed (response )
170+ body = None
171+ else :
172+ self .raise_if_needed (response )
173+ body = self .decode_response (response )
136174
137- return self .decode_response (response )
175+ if include_headers :
176+ return body , headers
177+ return body
138178
139179 @staticmethod
140180 def decode_response (response ):
0 commit comments