# Source code for jira.utils

    """Jira utils used internally."""
    import threading
    import warnings
    from typing import Any, Optional, cast

    from requests import Response
    from requests.structures import CaseInsensitiveDict as _CaseInsensitiveDict

    from jira.resilientsession import raise_on_error

    class CaseInsensitiveDict(_CaseInsensitiveDict):
        """A case-insensitive ``dict``-like object.

        DEPRECATED: use requests.structures.CaseInsensitiveDict directly.

        Implements all methods and operations of
        ``collections.MutableMapping`` as well as dict's ``copy``. Also
        provides ``lower_items``.

        All keys are expected to be strings. The structure remembers the
        case of the last key to be set, and ``iter(instance)``,
        ``keys()``, ``items()``, ``iterkeys()``
        will contain case-sensitive keys. However, querying and contains
        testing is case insensitive::

            cid = CaseInsensitiveDict()
            cid['Accept'] = 'application/json'
            cid['accept'] == 'application/json'  # True
            list(cid) == ['Accept']  # True

        For example, ``headers['content-encoding']`` will return the
        value of a ``'Content-Encoding'`` response header, regardless
        of how the header name was originally stored.

        If the constructor, ``.update``, or equality comparison
        operations are given keys that have equal ``.lower()`` s, the
        behavior is undefined.

        """

        def __init__(self, *args, **kwargs) -> None:
            warnings.warn(
                "Use requests.structures.CaseInsensitiveDict directly", DeprecationWarning
            )
            super().__init__(*args, **kwargs)

    def threaded_requests(requests):
        for fn, url, request_args in requests:
            th = threading.Thread(target=fn, args=(url,), kwargs=request_args, name=url)
            th.start()

        for th in threading.enumerate():
            if th.name.startswith("http"):
                th.join()

    def json_loads(r: Optional[Response]) -> Any:
        """Attempts to load json the result of a response

        Args:
            r (Optional[Response]): The Response object

        Raises:
            JIRAError: via :py:func:`jira.resilientsession.raise_on_error`

        Returns:
            Union[List[Dict[str, Any]], Dict[str, Any]]: the json
        """
        raise_on_error(r)  # if 'r' is None, will raise an error here
        r = cast(Response, r)  # tell mypy only Response-like are here
        try:
            return r.json()
        except ValueError:
            # json.loads() fails with empty bodies
            if not r.text:
                return {}
            raise