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