Source code for pandasdmx.remote

import logging
import os
from io import BufferedIOBase, BytesIO
from warnings import warn

import requests

try:
    from requests_cache import CachedSession as MaybeCachedSession
except ImportError:  # pragma: no cover
    warn(
        "optional dependency requests_cache is not installed; cache options "
        "to Session() have no effect",
        RuntimeWarning,
    )
    from requests import Session as MaybeCachedSession


logger = logging.getLogger(__name__)


[docs]class Session(MaybeCachedSession): """:class:`requests.Session` subclass with optional caching. If requests_cache is installed, this class caches responses. """ def __init__( self, proxies=None, stream=False, auth=None, cert=None, verify=True, **kwargs, ): if MaybeCachedSession is not requests.Session: # Using requests_cache.CachedSession # No cache keyword arguments supplied = don't use the cache disabled = set(kwargs.keys()) <= {"get_footer_url"} if disabled: # Avoid creating any file kwargs["backend"] = "memory" super(Session, self).__init__(**kwargs) # Overwrite value from requests_cache.CachedSession.__init__() self._is_cache_disabled = disabled elif len(kwargs): raise ValueError( "Cache arguments have no effect without " "requests_session: %s" % kwargs ) else: # Plain requests.Session super(Session, self).__init__() # Overwrite values from requests.Session.__init__() # TODO: consider passing these values to __init__, but manage # the ugly 'get_footer' stuff self.proxies = proxies self.stream = stream self.auth = auth self.cert = cert self.verify = verify
[docs]class ResponseIO(BufferedIOBase): """Buffered wrapper for :class:`requests.Response` with optional file output. :class:`ResponseIO` wraps a :class:`requests.Response` object's 'content' attribute, providing a file-like object from which bytes can be :meth:`read` incrementally. Parameters ---------- response : :class:`requests.Response` HTTP response to wrap. tee : binary, writable :py:class:`io.BufferedIOBase`, or :class:`fsspec.core.OpenFile` or :class:`io.PathLike`, defaults to io.BytesIO. If *tee* is an open binary file, it is used to store the received data. If *tee* is a PathLike, it is passed to :func:`open`, . *tee* is exposed as *self.tee* and not closed, so this class may be instantiated in a with-context. The latter is also recommended if a :class:`fsspec.core.OpenFile` is passed. """ def __init__(self, response, tee=None): self.response = response # Open a new file in various scenarios, or assume that tee is an open file if tee is None: tee = BytesIO() elif isinstance(tee, (str, os.PathLike)): tee = open(tee, mode="w+b") # Handle the special case of a fsspec.OpenFile if isinstance(tee, list): assert len(tee) == 1, ValueError(f"Only 1 file allowed, {len(tee)} given.") tee = tee[0] # Now tee must be an open file, including when passed as such # use tee as instance cache self.tee = tee # write content, but do not close the file. tee.write(response.content) tee.flush() tee.seek(0) def readable(self): return True def read(self, size=-1): """Read and return up to `size` bytes by calling ``self.tee.read()``.""" return self.tee.read(size) def close(self): self.tee.close()