jpayne@7: """ jpayne@7: Python HTTP library with thread-safe connection pooling, file post support, user friendly, and more jpayne@7: """ jpayne@7: jpayne@7: from __future__ import annotations jpayne@7: jpayne@7: # Set default logging handler to avoid "No handler found" warnings. jpayne@7: import logging jpayne@7: import sys jpayne@7: import typing jpayne@7: import warnings jpayne@7: from logging import NullHandler jpayne@7: jpayne@7: from . import exceptions jpayne@7: from ._base_connection import _TYPE_BODY jpayne@7: from ._collections import HTTPHeaderDict jpayne@7: from ._version import __version__ jpayne@7: from .connectionpool import HTTPConnectionPool, HTTPSConnectionPool, connection_from_url jpayne@7: from .filepost import _TYPE_FIELDS, encode_multipart_formdata jpayne@7: from .poolmanager import PoolManager, ProxyManager, proxy_from_url jpayne@7: from .response import BaseHTTPResponse, HTTPResponse jpayne@7: from .util.request import make_headers jpayne@7: from .util.retry import Retry jpayne@7: from .util.timeout import Timeout jpayne@7: jpayne@7: # Ensure that Python is compiled with OpenSSL 1.1.1+ jpayne@7: # If the 'ssl' module isn't available at all that's jpayne@7: # fine, we only care if the module is available. jpayne@7: try: jpayne@7: import ssl jpayne@7: except ImportError: jpayne@7: pass jpayne@7: else: jpayne@7: if not ssl.OPENSSL_VERSION.startswith("OpenSSL "): # Defensive: jpayne@7: warnings.warn( jpayne@7: "urllib3 v2 only supports OpenSSL 1.1.1+, currently " jpayne@7: f"the 'ssl' module is compiled with {ssl.OPENSSL_VERSION!r}. " jpayne@7: "See: https://github.com/urllib3/urllib3/issues/3020", jpayne@7: exceptions.NotOpenSSLWarning, jpayne@7: ) jpayne@7: elif ssl.OPENSSL_VERSION_INFO < (1, 1, 1): # Defensive: jpayne@7: raise ImportError( jpayne@7: "urllib3 v2 only supports OpenSSL 1.1.1+, currently " jpayne@7: f"the 'ssl' module is compiled with {ssl.OPENSSL_VERSION!r}. " jpayne@7: "See: https://github.com/urllib3/urllib3/issues/2168" jpayne@7: ) jpayne@7: jpayne@7: __author__ = "Andrey Petrov (andrey.petrov@shazow.net)" jpayne@7: __license__ = "MIT" jpayne@7: __version__ = __version__ jpayne@7: jpayne@7: __all__ = ( jpayne@7: "HTTPConnectionPool", jpayne@7: "HTTPHeaderDict", jpayne@7: "HTTPSConnectionPool", jpayne@7: "PoolManager", jpayne@7: "ProxyManager", jpayne@7: "HTTPResponse", jpayne@7: "Retry", jpayne@7: "Timeout", jpayne@7: "add_stderr_logger", jpayne@7: "connection_from_url", jpayne@7: "disable_warnings", jpayne@7: "encode_multipart_formdata", jpayne@7: "make_headers", jpayne@7: "proxy_from_url", jpayne@7: "request", jpayne@7: "BaseHTTPResponse", jpayne@7: ) jpayne@7: jpayne@7: logging.getLogger(__name__).addHandler(NullHandler()) jpayne@7: jpayne@7: jpayne@7: def add_stderr_logger( jpayne@7: level: int = logging.DEBUG, jpayne@7: ) -> logging.StreamHandler[typing.TextIO]: jpayne@7: """ jpayne@7: Helper for quickly adding a StreamHandler to the logger. Useful for jpayne@7: debugging. jpayne@7: jpayne@7: Returns the handler after adding it. jpayne@7: """ jpayne@7: # This method needs to be in this __init__.py to get the __name__ correct jpayne@7: # even if urllib3 is vendored within another package. jpayne@7: logger = logging.getLogger(__name__) jpayne@7: handler = logging.StreamHandler() jpayne@7: handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s %(message)s")) jpayne@7: logger.addHandler(handler) jpayne@7: logger.setLevel(level) jpayne@7: logger.debug("Added a stderr logging handler to logger: %s", __name__) jpayne@7: return handler jpayne@7: jpayne@7: jpayne@7: # ... Clean up. jpayne@7: del NullHandler jpayne@7: jpayne@7: jpayne@7: # All warning filters *must* be appended unless you're really certain that they jpayne@7: # shouldn't be: otherwise, it's very hard for users to use most Python jpayne@7: # mechanisms to silence them. jpayne@7: # SecurityWarning's always go off by default. jpayne@7: warnings.simplefilter("always", exceptions.SecurityWarning, append=True) jpayne@7: # InsecurePlatformWarning's don't vary between requests, so we keep it default. jpayne@7: warnings.simplefilter("default", exceptions.InsecurePlatformWarning, append=True) jpayne@7: jpayne@7: jpayne@7: def disable_warnings(category: type[Warning] = exceptions.HTTPWarning) -> None: jpayne@7: """ jpayne@7: Helper for quickly disabling all urllib3 warnings. jpayne@7: """ jpayne@7: warnings.simplefilter("ignore", category) jpayne@7: jpayne@7: jpayne@7: _DEFAULT_POOL = PoolManager() jpayne@7: jpayne@7: jpayne@7: def request( jpayne@7: method: str, jpayne@7: url: str, jpayne@7: *, jpayne@7: body: _TYPE_BODY | None = None, jpayne@7: fields: _TYPE_FIELDS | None = None, jpayne@7: headers: typing.Mapping[str, str] | None = None, jpayne@7: preload_content: bool | None = True, jpayne@7: decode_content: bool | None = True, jpayne@7: redirect: bool | None = True, jpayne@7: retries: Retry | bool | int | None = None, jpayne@7: timeout: Timeout | float | int | None = 3, jpayne@7: json: typing.Any | None = None, jpayne@7: ) -> BaseHTTPResponse: jpayne@7: """ jpayne@7: A convenience, top-level request method. It uses a module-global ``PoolManager`` instance. jpayne@7: Therefore, its side effects could be shared across dependencies relying on it. jpayne@7: To avoid side effects create a new ``PoolManager`` instance and use it instead. jpayne@7: The method does not accept low-level ``**urlopen_kw`` keyword arguments. jpayne@7: jpayne@7: :param method: jpayne@7: HTTP request method (such as GET, POST, PUT, etc.) jpayne@7: jpayne@7: :param url: jpayne@7: The URL to perform the request on. jpayne@7: jpayne@7: :param body: jpayne@7: Data to send in the request body, either :class:`str`, :class:`bytes`, jpayne@7: an iterable of :class:`str`/:class:`bytes`, or a file-like object. jpayne@7: jpayne@7: :param fields: jpayne@7: Data to encode and send in the request body. jpayne@7: jpayne@7: :param headers: jpayne@7: Dictionary of custom headers to send, such as User-Agent, jpayne@7: If-None-Match, etc. jpayne@7: jpayne@7: :param bool preload_content: jpayne@7: If True, the response's body will be preloaded into memory. jpayne@7: jpayne@7: :param bool decode_content: jpayne@7: If True, will attempt to decode the body based on the jpayne@7: 'content-encoding' header. jpayne@7: jpayne@7: :param redirect: jpayne@7: If True, automatically handle redirects (status codes 301, 302, jpayne@7: 303, 307, 308). Each redirect counts as a retry. Disabling retries jpayne@7: will disable redirect, too. jpayne@7: jpayne@7: :param retries: jpayne@7: Configure the number of retries to allow before raising a jpayne@7: :class:`~urllib3.exceptions.MaxRetryError` exception. jpayne@7: jpayne@7: If ``None`` (default) will retry 3 times, see ``Retry.DEFAULT``. Pass a jpayne@7: :class:`~urllib3.util.retry.Retry` object for fine-grained control jpayne@7: over different types of retries. jpayne@7: Pass an integer number to retry connection errors that many times, jpayne@7: but no other types of errors. Pass zero to never retry. jpayne@7: jpayne@7: If ``False``, then retries are disabled and any exception is raised jpayne@7: immediately. Also, instead of raising a MaxRetryError on redirects, jpayne@7: the redirect response will be returned. jpayne@7: jpayne@7: :type retries: :class:`~urllib3.util.retry.Retry`, False, or an int. jpayne@7: jpayne@7: :param timeout: jpayne@7: If specified, overrides the default timeout for this one jpayne@7: request. It may be a float (in seconds) or an instance of jpayne@7: :class:`urllib3.util.Timeout`. jpayne@7: jpayne@7: :param json: jpayne@7: Data to encode and send as JSON with UTF-encoded in the request body. jpayne@7: The ``"Content-Type"`` header will be set to ``"application/json"`` jpayne@7: unless specified otherwise. jpayne@7: """ jpayne@7: jpayne@7: return _DEFAULT_POOL.request( jpayne@7: method, jpayne@7: url, jpayne@7: body=body, jpayne@7: fields=fields, jpayne@7: headers=headers, jpayne@7: preload_content=preload_content, jpayne@7: decode_content=decode_content, jpayne@7: redirect=redirect, jpayne@7: retries=retries, jpayne@7: timeout=timeout, jpayne@7: json=json, jpayne@7: ) jpayne@7: jpayne@7: jpayne@7: if sys.platform == "emscripten": jpayne@7: from .contrib.emscripten import inject_into_urllib3 # noqa: 401 jpayne@7: jpayne@7: inject_into_urllib3()