jpayne@7
|
1 """
|
jpayne@7
|
2 Python HTTP library with thread-safe connection pooling, file post support, user friendly, and more
|
jpayne@7
|
3 """
|
jpayne@7
|
4
|
jpayne@7
|
5 from __future__ import annotations
|
jpayne@7
|
6
|
jpayne@7
|
7 # Set default logging handler to avoid "No handler found" warnings.
|
jpayne@7
|
8 import logging
|
jpayne@7
|
9 import sys
|
jpayne@7
|
10 import typing
|
jpayne@7
|
11 import warnings
|
jpayne@7
|
12 from logging import NullHandler
|
jpayne@7
|
13
|
jpayne@7
|
14 from . import exceptions
|
jpayne@7
|
15 from ._base_connection import _TYPE_BODY
|
jpayne@7
|
16 from ._collections import HTTPHeaderDict
|
jpayne@7
|
17 from ._version import __version__
|
jpayne@7
|
18 from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, connection_from_url
|
jpayne@7
|
19 from .filepost import _TYPE_FIELDS, encode_multipart_formdata
|
jpayne@7
|
20 from .poolmanager import PoolManager, ProxyManager, proxy_from_url
|
jpayne@7
|
21 from .response import BaseHTTPResponse, HTTPResponse
|
jpayne@7
|
22 from .util.request import make_headers
|
jpayne@7
|
23 from .util.retry import Retry
|
jpayne@7
|
24 from .util.timeout import Timeout
|
jpayne@7
|
25
|
jpayne@7
|
26 # Ensure that Python is compiled with OpenSSL 1.1.1+
|
jpayne@7
|
27 # If the 'ssl' module isn't available at all that's
|
jpayne@7
|
28 # fine, we only care if the module is available.
|
jpayne@7
|
29 try:
|
jpayne@7
|
30 import ssl
|
jpayne@7
|
31 except ImportError:
|
jpayne@7
|
32 pass
|
jpayne@7
|
33 else:
|
jpayne@7
|
34 if not ssl.OPENSSL_VERSION.startswith("OpenSSL "): # Defensive:
|
jpayne@7
|
35 warnings.warn(
|
jpayne@7
|
36 "urllib3 v2 only supports OpenSSL 1.1.1+, currently "
|
jpayne@7
|
37 f"the 'ssl' module is compiled with {ssl.OPENSSL_VERSION!r}. "
|
jpayne@7
|
38 "See: https://github.com/urllib3/urllib3/issues/3020",
|
jpayne@7
|
39 exceptions.NotOpenSSLWarning,
|
jpayne@7
|
40 )
|
jpayne@7
|
41 elif ssl.OPENSSL_VERSION_INFO < (1, 1, 1): # Defensive:
|
jpayne@7
|
42 raise ImportError(
|
jpayne@7
|
43 "urllib3 v2 only supports OpenSSL 1.1.1+, currently "
|
jpayne@7
|
44 f"the 'ssl' module is compiled with {ssl.OPENSSL_VERSION!r}. "
|
jpayne@7
|
45 "See: https://github.com/urllib3/urllib3/issues/2168"
|
jpayne@7
|
46 )
|
jpayne@7
|
47
|
jpayne@7
|
48 __author__ = "Andrey Petrov (andrey.petrov@shazow.net)"
|
jpayne@7
|
49 __license__ = "MIT"
|
jpayne@7
|
50 __version__ = __version__
|
jpayne@7
|
51
|
jpayne@7
|
52 __all__ = (
|
jpayne@7
|
53 "HTTPConnectionPool",
|
jpayne@7
|
54 "HTTPHeaderDict",
|
jpayne@7
|
55 "HTTPSConnectionPool",
|
jpayne@7
|
56 "PoolManager",
|
jpayne@7
|
57 "ProxyManager",
|
jpayne@7
|
58 "HTTPResponse",
|
jpayne@7
|
59 "Retry",
|
jpayne@7
|
60 "Timeout",
|
jpayne@7
|
61 "add_stderr_logger",
|
jpayne@7
|
62 "connection_from_url",
|
jpayne@7
|
63 "disable_warnings",
|
jpayne@7
|
64 "encode_multipart_formdata",
|
jpayne@7
|
65 "make_headers",
|
jpayne@7
|
66 "proxy_from_url",
|
jpayne@7
|
67 "request",
|
jpayne@7
|
68 "BaseHTTPResponse",
|
jpayne@7
|
69 )
|
jpayne@7
|
70
|
jpayne@7
|
71 logging.getLogger(__name__).addHandler(NullHandler())
|
jpayne@7
|
72
|
jpayne@7
|
73
|
jpayne@7
|
74 def add_stderr_logger(
|
jpayne@7
|
75 level: int = logging.DEBUG,
|
jpayne@7
|
76 ) -> logging.StreamHandler[typing.TextIO]:
|
jpayne@7
|
77 """
|
jpayne@7
|
78 Helper for quickly adding a StreamHandler to the logger. Useful for
|
jpayne@7
|
79 debugging.
|
jpayne@7
|
80
|
jpayne@7
|
81 Returns the handler after adding it.
|
jpayne@7
|
82 """
|
jpayne@7
|
83 # This method needs to be in this __init__.py to get the __name__ correct
|
jpayne@7
|
84 # even if urllib3 is vendored within another package.
|
jpayne@7
|
85 logger = logging.getLogger(__name__)
|
jpayne@7
|
86 handler = logging.StreamHandler()
|
jpayne@7
|
87 handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s"))
|
jpayne@7
|
88 logger.addHandler(handler)
|
jpayne@7
|
89 logger.setLevel(level)
|
jpayne@7
|
90 logger.debug("Added a stderr logging handler to logger: %s", __name__)
|
jpayne@7
|
91 return handler
|
jpayne@7
|
92
|
jpayne@7
|
93
|
jpayne@7
|
94 # ... Clean up.
|
jpayne@7
|
95 del NullHandler
|
jpayne@7
|
96
|
jpayne@7
|
97
|
jpayne@7
|
98 # All warning filters *must* be appended unless you're really certain that they
|
jpayne@7
|
99 # shouldn't be: otherwise, it's very hard for users to use most Python
|
jpayne@7
|
100 # mechanisms to silence them.
|
jpayne@7
|
101 # SecurityWarning's always go off by default.
|
jpayne@7
|
102 warnings.simplefilter("always", exceptions.SecurityWarning, append=True)
|
jpayne@7
|
103 # InsecurePlatformWarning's don't vary between requests, so we keep it default.
|
jpayne@7
|
104 warnings.simplefilter("default", exceptions.InsecurePlatformWarning, append=True)
|
jpayne@7
|
105
|
jpayne@7
|
106
|
jpayne@7
|
107 def disable_warnings(category: type[Warning] = exceptions.HTTPWarning) -> None:
|
jpayne@7
|
108 """
|
jpayne@7
|
109 Helper for quickly disabling all urllib3 warnings.
|
jpayne@7
|
110 """
|
jpayne@7
|
111 warnings.simplefilter("ignore", category)
|
jpayne@7
|
112
|
jpayne@7
|
113
|
jpayne@7
|
114 _DEFAULT_POOL = PoolManager()
|
jpayne@7
|
115
|
jpayne@7
|
116
|
jpayne@7
|
117 def request(
|
jpayne@7
|
118 method: str,
|
jpayne@7
|
119 url: str,
|
jpayne@7
|
120 *,
|
jpayne@7
|
121 body: _TYPE_BODY | None = None,
|
jpayne@7
|
122 fields: _TYPE_FIELDS | None = None,
|
jpayne@7
|
123 headers: typing.Mapping[str, str] | None = None,
|
jpayne@7
|
124 preload_content: bool | None = True,
|
jpayne@7
|
125 decode_content: bool | None = True,
|
jpayne@7
|
126 redirect: bool | None = True,
|
jpayne@7
|
127 retries: Retry | bool | int | None = None,
|
jpayne@7
|
128 timeout: Timeout | float | int | None = 3,
|
jpayne@7
|
129 json: typing.Any | None = None,
|
jpayne@7
|
130 ) -> BaseHTTPResponse:
|
jpayne@7
|
131 """
|
jpayne@7
|
132 A convenience, top-level request method. It uses a module-global ``PoolManager`` instance.
|
jpayne@7
|
133 Therefore, its side effects could be shared across dependencies relying on it.
|
jpayne@7
|
134 To avoid side effects create a new ``PoolManager`` instance and use it instead.
|
jpayne@7
|
135 The method does not accept low-level ``**urlopen_kw`` keyword arguments.
|
jpayne@7
|
136
|
jpayne@7
|
137 :param method:
|
jpayne@7
|
138 HTTP request method (such as GET, POST, PUT, etc.)
|
jpayne@7
|
139
|
jpayne@7
|
140 :param url:
|
jpayne@7
|
141 The URL to perform the request on.
|
jpayne@7
|
142
|
jpayne@7
|
143 :param body:
|
jpayne@7
|
144 Data to send in the request body, either :class:`str`, :class:`bytes`,
|
jpayne@7
|
145 an iterable of :class:`str`/:class:`bytes`, or a file-like object.
|
jpayne@7
|
146
|
jpayne@7
|
147 :param fields:
|
jpayne@7
|
148 Data to encode and send in the request body.
|
jpayne@7
|
149
|
jpayne@7
|
150 :param headers:
|
jpayne@7
|
151 Dictionary of custom headers to send, such as User-Agent,
|
jpayne@7
|
152 If-None-Match, etc.
|
jpayne@7
|
153
|
jpayne@7
|
154 :param bool preload_content:
|
jpayne@7
|
155 If True, the response's body will be preloaded into memory.
|
jpayne@7
|
156
|
jpayne@7
|
157 :param bool decode_content:
|
jpayne@7
|
158 If True, will attempt to decode the body based on the
|
jpayne@7
|
159 'content-encoding' header.
|
jpayne@7
|
160
|
jpayne@7
|
161 :param redirect:
|
jpayne@7
|
162 If True, automatically handle redirects (status codes 301, 302,
|
jpayne@7
|
163 303, 307, 308). Each redirect counts as a retry. Disabling retries
|
jpayne@7
|
164 will disable redirect, too.
|
jpayne@7
|
165
|
jpayne@7
|
166 :param retries:
|
jpayne@7
|
167 Configure the number of retries to allow before raising a
|
jpayne@7
|
168 :class:`~urllib3.exceptions.MaxRetryError` exception.
|
jpayne@7
|
169
|
jpayne@7
|
170 If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a
|
jpayne@7
|
171 :class:`~urllib3.util.retry.Retry` object for fine-grained control
|
jpayne@7
|
172 over different types of retries.
|
jpayne@7
|
173 Pass an integer number to retry connection errors that many times,
|
jpayne@7
|
174 but no other types of errors. Pass zero to never retry.
|
jpayne@7
|
175
|
jpayne@7
|
176 If ``False``, then retries are disabled and any exception is raised
|
jpayne@7
|
177 immediately. Also, instead of raising a MaxRetryError on redirects,
|
jpayne@7
|
178 the redirect response will be returned.
|
jpayne@7
|
179
|
jpayne@7
|
180 :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int.
|
jpayne@7
|
181
|
jpayne@7
|
182 :param timeout:
|
jpayne@7
|
183 If specified, overrides the default timeout for this one
|
jpayne@7
|
184 request. It may be a float (in seconds) or an instance of
|
jpayne@7
|
185 :class:`urllib3.util.Timeout`.
|
jpayne@7
|
186
|
jpayne@7
|
187 :param json:
|
jpayne@7
|
188 Data to encode and send as JSON with UTF-encoded in the request body.
|
jpayne@7
|
189 The ``"Content-Type"`` header will be set to ``"application/json"``
|
jpayne@7
|
190 unless specified otherwise.
|
jpayne@7
|
191 """
|
jpayne@7
|
192
|
jpayne@7
|
193 return _DEFAULT_POOL.request(
|
jpayne@7
|
194 method,
|
jpayne@7
|
195 url,
|
jpayne@7
|
196 body=body,
|
jpayne@7
|
197 fields=fields,
|
jpayne@7
|
198 headers=headers,
|
jpayne@7
|
199 preload_content=preload_content,
|
jpayne@7
|
200 decode_content=decode_content,
|
jpayne@7
|
201 redirect=redirect,
|
jpayne@7
|
202 retries=retries,
|
jpayne@7
|
203 timeout=timeout,
|
jpayne@7
|
204 json=json,
|
jpayne@7
|
205 )
|
jpayne@7
|
206
|
jpayne@7
|
207
|
jpayne@7
|
208 if sys.platform == "emscripten":
|
jpayne@7
|
209 from .contrib.emscripten import inject_into_urllib3 # noqa: 401
|
jpayne@7
|
210
|
jpayne@7
|
211 inject_into_urllib3()
|