Mercurial > repos > jpayne > bioproject_to_srr_2
comparison urllib3/util/timeout.py @ 7:5eb2d5e3bf22
planemo upload for repository https://toolrepo.galaxytrakr.org/view/jpayne/bioproject_to_srr_2/556cac4fb538
author | jpayne |
---|---|
date | Sun, 05 May 2024 23:32:17 -0400 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
6:b2745907b1eb | 7:5eb2d5e3bf22 |
---|---|
1 from __future__ import annotations | |
2 | |
3 import time | |
4 import typing | |
5 from enum import Enum | |
6 from socket import getdefaulttimeout | |
7 | |
8 from ..exceptions import TimeoutStateError | |
9 | |
10 if typing.TYPE_CHECKING: | |
11 from typing import Final | |
12 | |
13 | |
14 class _TYPE_DEFAULT(Enum): | |
15 # This value should never be passed to socket.settimeout() so for safety we use a -1. | |
16 # socket.settimout() raises a ValueError for negative values. | |
17 token = -1 | |
18 | |
19 | |
20 _DEFAULT_TIMEOUT: Final[_TYPE_DEFAULT] = _TYPE_DEFAULT.token | |
21 | |
22 _TYPE_TIMEOUT = typing.Optional[typing.Union[float, _TYPE_DEFAULT]] | |
23 | |
24 | |
25 class Timeout: | |
26 """Timeout configuration. | |
27 | |
28 Timeouts can be defined as a default for a pool: | |
29 | |
30 .. code-block:: python | |
31 | |
32 import urllib3 | |
33 | |
34 timeout = urllib3.util.Timeout(connect=2.0, read=7.0) | |
35 | |
36 http = urllib3.PoolManager(timeout=timeout) | |
37 | |
38 resp = http.request("GET", "https://example.com/") | |
39 | |
40 print(resp.status) | |
41 | |
42 Or per-request (which overrides the default for the pool): | |
43 | |
44 .. code-block:: python | |
45 | |
46 response = http.request("GET", "https://example.com/", timeout=Timeout(10)) | |
47 | |
48 Timeouts can be disabled by setting all the parameters to ``None``: | |
49 | |
50 .. code-block:: python | |
51 | |
52 no_timeout = Timeout(connect=None, read=None) | |
53 response = http.request("GET", "https://example.com/", timeout=no_timeout) | |
54 | |
55 | |
56 :param total: | |
57 This combines the connect and read timeouts into one; the read timeout | |
58 will be set to the time leftover from the connect attempt. In the | |
59 event that both a connect timeout and a total are specified, or a read | |
60 timeout and a total are specified, the shorter timeout will be applied. | |
61 | |
62 Defaults to None. | |
63 | |
64 :type total: int, float, or None | |
65 | |
66 :param connect: | |
67 The maximum amount of time (in seconds) to wait for a connection | |
68 attempt to a server to succeed. Omitting the parameter will default the | |
69 connect timeout to the system default, probably `the global default | |
70 timeout in socket.py | |
71 <http://hg.python.org/cpython/file/603b4d593758/Lib/socket.py#l535>`_. | |
72 None will set an infinite timeout for connection attempts. | |
73 | |
74 :type connect: int, float, or None | |
75 | |
76 :param read: | |
77 The maximum amount of time (in seconds) to wait between consecutive | |
78 read operations for a response from the server. Omitting the parameter | |
79 will default the read timeout to the system default, probably `the | |
80 global default timeout in socket.py | |
81 <http://hg.python.org/cpython/file/603b4d593758/Lib/socket.py#l535>`_. | |
82 None will set an infinite timeout. | |
83 | |
84 :type read: int, float, or None | |
85 | |
86 .. note:: | |
87 | |
88 Many factors can affect the total amount of time for urllib3 to return | |
89 an HTTP response. | |
90 | |
91 For example, Python's DNS resolver does not obey the timeout specified | |
92 on the socket. Other factors that can affect total request time include | |
93 high CPU load, high swap, the program running at a low priority level, | |
94 or other behaviors. | |
95 | |
96 In addition, the read and total timeouts only measure the time between | |
97 read operations on the socket connecting the client and the server, | |
98 not the total amount of time for the request to return a complete | |
99 response. For most requests, the timeout is raised because the server | |
100 has not sent the first byte in the specified time. This is not always | |
101 the case; if a server streams one byte every fifteen seconds, a timeout | |
102 of 20 seconds will not trigger, even though the request will take | |
103 several minutes to complete. | |
104 """ | |
105 | |
106 #: A sentinel object representing the default timeout value | |
107 DEFAULT_TIMEOUT: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT | |
108 | |
109 def __init__( | |
110 self, | |
111 total: _TYPE_TIMEOUT = None, | |
112 connect: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, | |
113 read: _TYPE_TIMEOUT = _DEFAULT_TIMEOUT, | |
114 ) -> None: | |
115 self._connect = self._validate_timeout(connect, "connect") | |
116 self._read = self._validate_timeout(read, "read") | |
117 self.total = self._validate_timeout(total, "total") | |
118 self._start_connect: float | None = None | |
119 | |
120 def __repr__(self) -> str: | |
121 return f"{type(self).__name__}(connect={self._connect!r}, read={self._read!r}, total={self.total!r})" | |
122 | |
123 # __str__ provided for backwards compatibility | |
124 __str__ = __repr__ | |
125 | |
126 @staticmethod | |
127 def resolve_default_timeout(timeout: _TYPE_TIMEOUT) -> float | None: | |
128 return getdefaulttimeout() if timeout is _DEFAULT_TIMEOUT else timeout | |
129 | |
130 @classmethod | |
131 def _validate_timeout(cls, value: _TYPE_TIMEOUT, name: str) -> _TYPE_TIMEOUT: | |
132 """Check that a timeout attribute is valid. | |
133 | |
134 :param value: The timeout value to validate | |
135 :param name: The name of the timeout attribute to validate. This is | |
136 used to specify in error messages. | |
137 :return: The validated and casted version of the given value. | |
138 :raises ValueError: If it is a numeric value less than or equal to | |
139 zero, or the type is not an integer, float, or None. | |
140 """ | |
141 if value is None or value is _DEFAULT_TIMEOUT: | |
142 return value | |
143 | |
144 if isinstance(value, bool): | |
145 raise ValueError( | |
146 "Timeout cannot be a boolean value. It must " | |
147 "be an int, float or None." | |
148 ) | |
149 try: | |
150 float(value) | |
151 except (TypeError, ValueError): | |
152 raise ValueError( | |
153 "Timeout value %s was %s, but it must be an " | |
154 "int, float or None." % (name, value) | |
155 ) from None | |
156 | |
157 try: | |
158 if value <= 0: | |
159 raise ValueError( | |
160 "Attempted to set %s timeout to %s, but the " | |
161 "timeout cannot be set to a value less " | |
162 "than or equal to 0." % (name, value) | |
163 ) | |
164 except TypeError: | |
165 raise ValueError( | |
166 "Timeout value %s was %s, but it must be an " | |
167 "int, float or None." % (name, value) | |
168 ) from None | |
169 | |
170 return value | |
171 | |
172 @classmethod | |
173 def from_float(cls, timeout: _TYPE_TIMEOUT) -> Timeout: | |
174 """Create a new Timeout from a legacy timeout value. | |
175 | |
176 The timeout value used by httplib.py sets the same timeout on the | |
177 connect(), and recv() socket requests. This creates a :class:`Timeout` | |
178 object that sets the individual timeouts to the ``timeout`` value | |
179 passed to this function. | |
180 | |
181 :param timeout: The legacy timeout value. | |
182 :type timeout: integer, float, :attr:`urllib3.util.Timeout.DEFAULT_TIMEOUT`, or None | |
183 :return: Timeout object | |
184 :rtype: :class:`Timeout` | |
185 """ | |
186 return Timeout(read=timeout, connect=timeout) | |
187 | |
188 def clone(self) -> Timeout: | |
189 """Create a copy of the timeout object | |
190 | |
191 Timeout properties are stored per-pool but each request needs a fresh | |
192 Timeout object to ensure each one has its own start/stop configured. | |
193 | |
194 :return: a copy of the timeout object | |
195 :rtype: :class:`Timeout` | |
196 """ | |
197 # We can't use copy.deepcopy because that will also create a new object | |
198 # for _GLOBAL_DEFAULT_TIMEOUT, which socket.py uses as a sentinel to | |
199 # detect the user default. | |
200 return Timeout(connect=self._connect, read=self._read, total=self.total) | |
201 | |
202 def start_connect(self) -> float: | |
203 """Start the timeout clock, used during a connect() attempt | |
204 | |
205 :raises urllib3.exceptions.TimeoutStateError: if you attempt | |
206 to start a timer that has been started already. | |
207 """ | |
208 if self._start_connect is not None: | |
209 raise TimeoutStateError("Timeout timer has already been started.") | |
210 self._start_connect = time.monotonic() | |
211 return self._start_connect | |
212 | |
213 def get_connect_duration(self) -> float: | |
214 """Gets the time elapsed since the call to :meth:`start_connect`. | |
215 | |
216 :return: Elapsed time in seconds. | |
217 :rtype: float | |
218 :raises urllib3.exceptions.TimeoutStateError: if you attempt | |
219 to get duration for a timer that hasn't been started. | |
220 """ | |
221 if self._start_connect is None: | |
222 raise TimeoutStateError( | |
223 "Can't get connect duration for timer that has not started." | |
224 ) | |
225 return time.monotonic() - self._start_connect | |
226 | |
227 @property | |
228 def connect_timeout(self) -> _TYPE_TIMEOUT: | |
229 """Get the value to use when setting a connection timeout. | |
230 | |
231 This will be a positive float or integer, the value None | |
232 (never timeout), or the default system timeout. | |
233 | |
234 :return: Connect timeout. | |
235 :rtype: int, float, :attr:`Timeout.DEFAULT_TIMEOUT` or None | |
236 """ | |
237 if self.total is None: | |
238 return self._connect | |
239 | |
240 if self._connect is None or self._connect is _DEFAULT_TIMEOUT: | |
241 return self.total | |
242 | |
243 return min(self._connect, self.total) # type: ignore[type-var] | |
244 | |
245 @property | |
246 def read_timeout(self) -> float | None: | |
247 """Get the value for the read timeout. | |
248 | |
249 This assumes some time has elapsed in the connection timeout and | |
250 computes the read timeout appropriately. | |
251 | |
252 If self.total is set, the read timeout is dependent on the amount of | |
253 time taken by the connect timeout. If the connection time has not been | |
254 established, a :exc:`~urllib3.exceptions.TimeoutStateError` will be | |
255 raised. | |
256 | |
257 :return: Value to use for the read timeout. | |
258 :rtype: int, float or None | |
259 :raises urllib3.exceptions.TimeoutStateError: If :meth:`start_connect` | |
260 has not yet been called on this object. | |
261 """ | |
262 if ( | |
263 self.total is not None | |
264 and self.total is not _DEFAULT_TIMEOUT | |
265 and self._read is not None | |
266 and self._read is not _DEFAULT_TIMEOUT | |
267 ): | |
268 # In case the connect timeout has not yet been established. | |
269 if self._start_connect is None: | |
270 return self._read | |
271 return max(0, min(self.total - self.get_connect_duration(), self._read)) | |
272 elif self.total is not None and self.total is not _DEFAULT_TIMEOUT: | |
273 return max(0, self.total - self.get_connect_duration()) | |
274 else: | |
275 return self.resolve_default_timeout(self._read) |