|
9 | 9 | from typing import TYPE_CHECKING |
10 | 10 | from urllib.parse import urlencode |
11 | 11 |
|
12 | | -from pyrate_limiter import Duration, Limiter, Rate |
13 | | -from requests import Session |
14 | | -from requests.adapters import HTTPAdapter, Retry |
15 | | -from requests_ratelimiter import LimiterAdapter |
16 | | -from requests_ratelimiter.requests_ratelimiter import HostBucketFactory |
17 | | - |
18 | 12 | if TYPE_CHECKING: |
19 | 13 | from asyncio import AbstractEventLoop |
20 | 14 | from collections.abc import Mapping |
21 | 15 | from typing import Any |
22 | 16 |
|
| 17 | +if sys.platform != "emscripten": |
| 18 | + from pyrate_limiter import Duration, Limiter, Rate |
| 19 | + from requests import Session |
| 20 | + from requests.adapters import HTTPAdapter, Retry |
| 21 | + from requests_ratelimiter import LimiterAdapter |
| 22 | + from requests_ratelimiter.requests_ratelimiter import HostBucketFactory |
| 23 | + |
23 | 24 |
|
24 | 25 | @dataclass |
25 | 26 | class _BrowserResponse: |
@@ -63,48 +64,60 @@ async def wait(self) -> None: |
63 | 64 | state.next_start = loop.time() + self.interval |
64 | 65 |
|
65 | 66 |
|
66 | | -_DEFAULT_TIMEOUT = (5.0, 20.0) |
67 | | - |
68 | | - |
69 | | -class _TimeoutSession(Session): |
70 | | - """Requests session that applies a bounded timeout by default.""" |
71 | | - |
72 | | - def request(self, method, url, **kwargs): |
73 | | - """Send a request with the shared default timeout unless overridden.""" |
74 | | - kwargs.setdefault("timeout", _DEFAULT_TIMEOUT) |
75 | | - return super().request(method, url, **kwargs) |
| 67 | +class _BrowserSession: |
| 68 | + """Reject synchronous HTTP calls in browser runtimes.""" |
| 69 | + |
| 70 | + def get(self, *args: Any, **kwargs: Any) -> Any: |
| 71 | + """Raise because Pyodide networking must use the asynchronous transport.""" |
| 72 | + raise RuntimeError( |
| 73 | + "Synchronous HTTP is unavailable in Pyodide; use async_get instead." |
| 74 | + ) |
| 75 | + |
| 76 | + |
| 77 | +if sys.platform != "emscripten": |
| 78 | + _DEFAULT_TIMEOUT = (5.0, 20.0) |
| 79 | + |
| 80 | + class _TimeoutSession(Session): |
| 81 | + """Requests session that applies a bounded timeout by default.""" |
| 82 | + |
| 83 | + def request(self, method, url, **kwargs): |
| 84 | + """Send a request with the shared default timeout unless overridden.""" |
| 85 | + kwargs.setdefault("timeout", _DEFAULT_TIMEOUT) |
| 86 | + return super().request(method, url, **kwargs) |
| 87 | + |
| 88 | + SESSION = _TimeoutSession() |
| 89 | + |
| 90 | + # retry logic |
| 91 | + retries = Retry( |
| 92 | + total=5, |
| 93 | + backoff_factor=0.1, |
| 94 | + status_forcelist=[ |
| 95 | + 429, |
| 96 | + 500, |
| 97 | + 502, |
| 98 | + 503, |
| 99 | + 504, |
| 100 | + ], |
| 101 | + ) |
| 102 | + |
| 103 | + adapter_ncbi = LimiterAdapter(per_second=3, max_retries=retries) |
| 104 | + SESSION.mount("https://www.ncbi.nlm.nih.gov/pmc/utils/", adapter_ncbi) |
| 105 | + SESSION.mount("https://eutils.ncbi.nlm.nih.gov/", adapter_ncbi) |
| 106 | + adapter_crossref = LimiterAdapter(per_second=50, max_retries=retries) |
| 107 | + SESSION.mount("https://api.crossref.org/", adapter_crossref) |
| 108 | + adapter_arxiv = LimiterAdapter( |
| 109 | + limiter=Limiter(HostBucketFactory([Rate(1, Duration.SECOND * 3)])), |
| 110 | + burst=1, |
| 111 | + max_retries=retries, |
| 112 | + ) |
| 113 | + SESSION.mount("https://export.arxiv.org/api", adapter_arxiv) |
| 114 | + adapter_semanticscholar = LimiterAdapter(per_second=1, max_retries=retries) |
| 115 | + SESSION.mount("https://api.semanticscholar.org/", adapter_semanticscholar) |
| 116 | + SESSION.mount("https://", HTTPAdapter(max_retries=retries)) |
| 117 | +else: |
| 118 | + SESSION = _BrowserSession() |
76 | 119 |
|
77 | 120 |
|
78 | | -SESSION = _TimeoutSession() |
79 | | - |
80 | | -# retry logic |
81 | | -retries = Retry( |
82 | | - total=5, |
83 | | - backoff_factor=0.1, |
84 | | - status_forcelist=[ |
85 | | - 429, |
86 | | - 500, |
87 | | - 502, |
88 | | - 503, |
89 | | - 504, |
90 | | - ], |
91 | | -) |
92 | | - |
93 | | -adapter_ncbi = LimiterAdapter(per_second=3, max_retries=retries) |
94 | | -SESSION.mount("https://www.ncbi.nlm.nih.gov/pmc/utils/", adapter_ncbi) |
95 | | -SESSION.mount("https://eutils.ncbi.nlm.nih.gov/", adapter_ncbi) |
96 | | -adapter_crossref = LimiterAdapter(per_second=50, max_retries=retries) |
97 | | -SESSION.mount("https://api.crossref.org/", adapter_crossref) |
98 | | -adapter_arxiv = LimiterAdapter( |
99 | | - limiter=Limiter(HostBucketFactory([Rate(1, Duration.SECOND * 3)])), |
100 | | - burst=1, |
101 | | - max_retries=retries, |
102 | | -) |
103 | | -SESSION.mount("https://export.arxiv.org/api", adapter_arxiv) |
104 | | -adapter_semanticscholar = LimiterAdapter(per_second=1, max_retries=retries) |
105 | | -SESSION.mount("https://api.semanticscholar.org/", adapter_semanticscholar) |
106 | | -SESSION.mount("https://", HTTPAdapter(max_retries=retries)) |
107 | | - |
108 | 121 | _BROWSER_RETRY_STATUSES = {429, 500, 502, 503, 504} |
109 | 122 | _BROWSER_TIMEOUT = 20.0 |
110 | 123 | _BROWSER_RETRIES = 2 |
|
0 commit comments