Pyzotero: a Python client for the Zotero API pyzotero.readthedocs.io
zotero

Extract get_backoff_duration utility function

+31 -34
+11 -24
src/pyzotero/_client.py
··· 37 37 ONE_HOUR, 38 38 build_url, 39 39 chunks, 40 + get_backoff_duration, 40 41 merge_params, 41 42 token, 42 43 ) ··· 291 292 self.request.raise_for_status() 292 293 except httpx.HTTPError as exc: 293 294 error_handler(self, self.request, exc) 294 - backoff = self.request.headers.get("backoff") or self.request.headers.get( 295 - "retry-after", 296 - ) 295 + backoff = get_backoff_duration(self.request.headers) 297 296 if backoff: 298 297 self._set_backoff(backoff) 299 298 return self.request ··· 355 354 req.raise_for_status() 356 355 except httpx.HTTPError as exc: 357 356 error_handler(self, req, exc) 358 - backoff = self.request.headers.get("backoff") or self.request.headers.get( 359 - "retry-after", 360 - ) 357 + backoff = get_backoff_duration(self.request.headers) 361 358 if backoff: 362 359 self._set_backoff(backoff) 363 360 return req.status_code == httpx.codes.NOT_MODIFIED ··· 507 504 resp.raise_for_status() 508 505 except httpx.HTTPError as exc: 509 506 error_handler(self, resp, exc) 510 - backoff = self.request.headers.get("backoff") or self.request.headers.get( 511 - "retry-after", 512 - ) 507 + backoff = get_backoff_duration(self.request.headers) 513 508 if backoff: 514 509 self._set_backoff(backoff) 515 510 return resp.json() ··· 874 869 req.raise_for_status() 875 870 except httpx.HTTPError as exc: 876 871 error_handler(self, req, exc) 877 - backoff = self.request.headers.get("backoff") or self.request.headers.get( 878 - "retry-after", 879 - ) 872 + backoff = get_backoff_duration(self.request.headers) 880 873 if backoff: 881 874 self._set_backoff(backoff) 882 875 return req.json() ··· 902 895 req.raise_for_status() 903 896 except httpx.HTTPError as exc: 904 897 error_handler(self, req, exc) 905 - backoff = self.request.headers.get("backoff") or self.request.headers.get( 906 - "retry-after", 907 - ) 898 + backoff = get_backoff_duration(self.request.headers) 908 899 if backoff: 909 900 self._set_backoff(backoff) 910 901 return req.status_code ··· 1082 1073 except httpx.HTTPError as exc: 1083 1074 error_handler(self, req, exc) 1084 1075 resp = req.json() 1085 - backoff = self.request.headers.get("backoff") or self.request.headers.get( 1086 - "retry-after", 1087 - ) 1076 + backoff = get_backoff_duration(self.request.headers) 1088 1077 if backoff: 1089 1078 self._set_backoff(backoff) 1090 1079 if parentid: ··· 1109 1098 presp.raise_for_status() 1110 1099 except httpx.HTTPError as exc: 1111 1100 error_handler(self, presp, exc) 1112 - backoff = presp.headers.get("backoff") or presp.headers.get( 1113 - "retry-after", 1114 - ) 1101 + backoff = get_backoff_duration(presp.headers) 1115 1102 if backoff: 1116 1103 self._set_backoff(backoff) 1117 1104 return resp ··· 1152 1139 req.raise_for_status() 1153 1140 except httpx.HTTPError as exc: 1154 1141 error_handler(self, req, exc) 1155 - backoff = req.headers.get("backoff") or req.headers.get("retry-after") 1142 + backoff = get_backoff_duration(req.headers) 1156 1143 if backoff: 1157 1144 self._set_backoff(backoff) 1158 1145 return req.json() ··· 1253 1240 req.raise_for_status() 1254 1241 except httpx.HTTPError as exc: 1255 1242 error_handler(self, req, exc) 1256 - backoff = req.headers.get("backoff") or req.headers.get("retry-after") 1243 + backoff = get_backoff_duration(req.headers) 1257 1244 if backoff: 1258 1245 self._set_backoff(backoff) 1259 1246 return True ··· 1279 1266 req.raise_for_status() 1280 1267 except httpx.HTTPError as exc: 1281 1268 error_handler(self, req, exc) 1282 - backoff = req.headers.get("backoff") or req.headers.get("retry-after") 1269 + backoff = get_backoff_duration(req.headers) 1283 1270 if backoff: 1284 1271 self._set_backoff(backoff) 1285 1272 return True
+2 -2
src/pyzotero/_decorators.py
··· 17 17 import httpx 18 18 from httpx import Request 19 19 20 - from ._utils import DEFAULT_TIMEOUT, build_url 20 + from ._utils import DEFAULT_TIMEOUT, build_url, get_backoff_duration 21 21 from .errors import error_handler 22 22 23 23 if TYPE_CHECKING: ··· 91 91 except httpx.HTTPError as exc: 92 92 error_handler(self, resp, exc) 93 93 self.request = resp 94 - backoff = resp.headers.get("backoff") or resp.headers.get("retry-after") 94 + backoff = get_backoff_duration(resp.headers) 95 95 if backoff: 96 96 self._set_backoff(backoff) 97 97
+5 -7
src/pyzotero/_upload.py
··· 17 17 import pyzotero as pz 18 18 19 19 from . import errors as ze 20 - from ._utils import build_url, token 20 + from ._utils import build_url, get_backoff_duration, token 21 21 from .errors import error_handler 22 22 23 23 if TYPE_CHECKING: ··· 107 107 req.raise_for_status() 108 108 except httpx.HTTPError as exc: 109 109 error_handler(self.zinstance, req, exc) 110 - backoff = req.headers.get("backoff") or req.headers.get("retry-after") 110 + backoff = get_backoff_duration(req.headers) 111 111 if backoff: 112 112 self.zinstance._set_backoff(backoff) 113 113 data = req.json() ··· 152 152 auth_req.raise_for_status() 153 153 except httpx.HTTPError as exc: 154 154 error_handler(self.zinstance, auth_req, exc) 155 - backoff = auth_req.headers.get("backoff") or auth_req.headers.get("retry-after") 155 + backoff = get_backoff_duration(auth_req.headers) 156 156 if backoff: 157 157 self.zinstance._set_backoff(backoff) 158 158 return auth_req.json() ··· 189 189 upload.raise_for_status() 190 190 except httpx.HTTPError as exc: 191 191 error_handler(self.zinstance, upload, exc) 192 - backoff = upload.headers.get("backoff") or upload.headers.get("retry-after") 192 + backoff = get_backoff_duration(upload.headers) 193 193 if backoff: 194 194 self.zinstance._set_backoff(backoff) 195 195 # now check the responses ··· 215 215 upload_reg.raise_for_status() 216 216 except httpx.HTTPError as exc: 217 217 error_handler(self.zinstance, upload_reg, exc) 218 - backoff = upload_reg.headers.get("backoff") or upload_reg.headers.get( 219 - "retry-after", 220 - ) 218 + backoff = get_backoff_duration(upload_reg.headers) 221 219 if backoff: 222 220 self.zinstance._set_backoff(backoff) 223 221
+10
src/pyzotero/_utils.py
··· 64 64 yield iterable[i : i + n] 65 65 66 66 67 + def get_backoff_duration(headers) -> str | None: 68 + """Extract backoff duration from response headers. 69 + 70 + The Zotero API may return backoff instructions via either the 71 + 'Backoff' or 'Retry-After' header. 72 + """ 73 + return headers.get("backoff") or headers.get("retry-after") 74 + 75 + 67 76 __all__ = [ 68 77 "DEFAULT_ITEM_LIMIT", 69 78 "DEFAULT_NUM_ITEMS", ··· 71 80 "ONE_HOUR", 72 81 "build_url", 73 82 "chunks", 83 + "get_backoff_duration", 74 84 "merge_params", 75 85 "token", 76 86 ]
+3 -1
src/pyzotero/errors.py
··· 10 10 11 11 import httpx 12 12 13 + from ._utils import get_backoff_duration 14 + 13 15 if TYPE_CHECKING: 14 16 from typing import Any 15 17 ··· 140 142 # check to see whether its 429 141 143 if req.status_code == httpx.codes.TOO_MANY_REQUESTS: 142 144 # try to get backoff or delay duration 143 - delay = req.headers.get("backoff") or req.headers.get("retry-after") 145 + delay = get_backoff_duration(req.headers) 144 146 if not delay: 145 147 msg = ( 146 148 "You are being rate-limited and no backoff or retry duration "