text: """
  This module is the core module of the Python client. It handles all the
  requests made to the API and parses the response in order make the
  interactions with the API as easy as possible. The initialization of the
  RadarlyApi's object defined here is the first step to start using the API.
  We advise you to use a default RadarlyAPI object otherwise you must specify
  a RadarlyApi object each time you want to retrieve data from the API.
  To initialize a default API, simply run:
  
  >>> from radarly import RadarlyApi
  >>> RadarlyApi.init(client_id=<client_id>, client_secret=<client_secret>)
  """
  
  import json
  from datetime import datetime
  from os import getenv
  
  import requests
  
  from .auth import RadarlyAuth
  from .exceptions import (AuthenticationError, NoInitializedApi,
                           RadarlyHTTPError, RateReached)
  from .rate import RateLimit
  from .utils._internal import _parse_error_response
  from .utils.jsonparser import snake_dict as _decoder, _BLACKLIST_PATH
  from .utils.router import Router
  
  __all__ = ['RadarlyApi']
  
  
  class RadarlyApi: # pylint: disable=R0902
      """Main interface with the Radarly API. It defines several methods in
      order to ease the interactions with the Radarly API. For example, it
      defines the methods in order to authenticate to the API or to refresh the
      tokens. Thanks to the current implementation of the package, you can use
      ``radarly`` without using any of the ``RadarlyApi``'s methods.
  
      Args:
          client_id (str): client ID which is given by Linkfluence in order to
              use the API. Can be set at initialization. If this argument is
              set to None during initialization, it will try to recover the
              RADARLY_CLIENT_ID environment variable.
          client_secret (str): code given by Linkfluence in order to ensure that
              the right user is using the API. Can be set at initialization.
              If this argument is set to None during initialization, it will
              try to recover the RADARLY_CLIENT_SECRET environment variable.
          scope (list[str]): scopes of the API. If None, the default value will
              be used (which is ``['listening', 'social-performance']``). Can be
              set at initialization.
          version (str): version of the API you want to use. Can be set at
              initialization. Default to ``1.1``.
          access_token (str): code generated by the API in order to authenticate
              each request. This code is automatically retrieved by the
              RadarlyApi during its initilization. The access token has a limited
              lifetime and must be refreshed when it has expired.
          refresh_token (str): code used to generate a new access token when
              you've got an expired token. The RadarlyApi can automatically use
              this refresh token to update the access token when it's expired.
          autorefresh (bool, optional): Whether or not refresh the access token
              when it's expired. Can be set during initialization. Default to True.
          timeout (float): maximum duration during which the API will wait for an
              answer from the Radarly's server. Can be set at initialization.
          proxies (dict): proxies used for the requests. The proxies must be set
              in the same way that in the ``requests`` module. Can be set at
              initialization.
          rates (RateLimit): RateLimit object which can be used to know the
              current number of requests made and how many left you can do.
      """
      _default_api = None
  
      def __init__(self, # pylint: disable=C0303
                   client_id=None,
                   client_secret=None,
                   scope=None,
                   autorefresh=True,
                   timeout=None,
                   proxies=None,
                   version='1.1',
                   router=None,
                   environment='prod',
                   authenticate=True):
          client_id = client_id or getenv('RADARLY_CLIENT_ID')
          client_secret = client_secret or getenv('RADARLY_CLIENT_SECRET')
          if not(client_id and client_secret):
              raise KeyError(("Neither client_id nor client_secret has been set "
                              "during the initialization of the API. Furthermore "
                              "RADARLY_CLIENT_ID and RADARLY_CLIENT_SECRET "
                              "variables have not be found in environment "
                              "variables. You must either specify the client_id "
                              "and client_secret arguments or set the right "
                              "environment variables to start using the "
                              "client."))
          self.client_id = client_id
          self.client_secret = client_secret
          self.access_token = None
          self.refresh_token = None
          self.timeout = timeout
          self.proxies = proxies
          self.autorefresh = autorefresh
          self.last_refresh = datetime.now()
          self._auth = None
          self.rates = RateLimit()
          self.scope = scope or [
              'listening',
              'social-performance',
          ]
          self.version = version
          self.environment = environment
          self.router = router or \
              type('Router', Router.__bases__, dict(Router.__dict__))
          if authenticate:
              self.authenticate()
  
      def __repr__(self):
          return '<RadarlyAPI.client_id={.client_id}>'.format(self)
  
      @classmethod
      def init(cls, *args, **kwargs):
          """
          Initialize a RadarlyAPI and set it as default api. The default API is
          the one that will be used each time you made a request if no API is
          passed in the parameter.
  
          Args:
              client_id (str): client_id you want to use to access data
              client_secret (str): secret token given by Linkfluence.
              autorefresh (bool, optional): whether or not refresh automatically
                  the acces_token when it's expired.
              scope (list[str]): list of scope
              timeout (float): timeout for the requests
              proxies (dict): proxies to use for the requests.
          """
          api = cls(*args, **kwargs)
          cls.set_default_api(api)
          return api
  
      @classmethod
      def get_default_api(cls):
          """
          Retrieve the default api previously set by the user.
  
          Raises:
              AssertionError: raised if no api was set as default api.
                  Use ``init`` method to set a default API.
          Returns:
              RadarlyApi: api object you have previously initialized
          """
          if cls._default_api is None:
              raise NoInitializedApi
          return cls._default_api
  
      @classmethod
      def set_default_api(cls, api):
          """Set a default API for the client. This method is automatically
          called when you use the ``init`` class method."""
          assert isinstance(api, cls), ("Only a RadarlyApi object can "
                                        "be set as default api")
          cls._default_api = api
          return None
  
      def request(self, verb, url, **kwargs):
          """
          Send a request using the request module. Some pre- and post-tasks
          are computed each time in order to actualize the rates information
          and check whether or not the request is a success. This method uses
          the same parameters as request function of requests module so you can
          easily made your own authenticated request.
  
          Args:
              verb (string): method used for the request
              url (string): url to ask
              **kwargs: keywords arguments sent with request
          Raises:
              HTTP Error: raised if the request failed for an unknown cause
          Returns:
              dict: corresponds to the response data of the answer
          """
          root_url = self.router.host[self.environment]
          url = url.strip('/')
          if self._auth is None:
              self.authenticate()
          if self.version and not url.startswith(self.version) \
              and not url.startswith(root_url):
              url = '{}/{}'.format(self.version, url)
          url = url if root_url in url  \
              else '{}/{}'.format(root_url, url)
          kwargs.setdefault('headers', {})
          kwargs['headers'].setdefault('Content-Type', 'application/json')
          if ('data' in kwargs and
                  kwargs['headers']['Content-Type'] == 'application/json'):
              kwargs['data'] = json.dumps(kwargs['data'])
          kwargs.setdefault('timeout', self.timeout)
          kwargs.setdefault('proxies', self.proxies)
          if self.rates.is_reached(url):
              raise RateReached('No more request available')
  
          res = requests.request(verb, url, auth=self._auth, **kwargs)
  
          error_data = _parse_error_response(res)
          error_type = error_data.get('error_type', '')
          if self.autorefresh and error_type == 'ExpiredTokenException':
              self.refresh()
              res = requests.request(verb, url, auth=self._auth, **kwargs)
          if not res.ok:
              raise RadarlyHTTPError(response=res)
  
          self.rates.update(url, res.headers)
  
          return _decoder(res.json(), blacklist=_BLACKLIST_PATH)
  
      def get(self, url, **kwargs):
          """Shortcut for the ``request`` method with 'GET' as verb.
  
          Args:
              url (string): url to ask
              **kwargs: keywords arguments sent with ``request``
          Raises:
              HTTP Error: raised if the request failed for an unknown cause
          Returns:
              dict: corresponds to the response data of the answer
          """
          return self.request('GET', url, **kwargs)
  
      def post(self, url, **kwargs):
          """Shortcut for the ``request`` method with 'POST' as verb.
  
          Args:
              url (string): url to ask
              **kwargs: keywords arguments sent with request
          Raises:
              HTTP Error: raised if the request failed for an unknown cause
          Returns:
              dict: corresponds to the response data of the answer
          """
          return self.request('POST', url, **kwargs)
  
      def put(self, url, **kwargs):
          """Shortcut for the ``request`` method with 'PUT' as verb.
  
          Args:
              url (string): url to ask
              **kwargs: keywords arguments sent with request
          Raises:
              HTTP Error: raised if the request failed for an unknown cause
          Returns:
              dict: corresponds to the response data of the answer
          """
          return self.request('PUT', url, **kwargs)
  
      def authenticate(self):
          """
          Enable the authentification to Radarly API. The Radarly API is
          currently using the OAUTH2 system for authorization : the
          (client_id, client_secret) will be used to generate an access_token
          and a refresh_token.
  
          Raises:
              BadAuthentication: raised if client_id or client_secret is
                  incorrect
          Returns:
              None:
          """
          data = dict(
              client_id=self.client_id,
              client_secret=self.client_secret,
              grant_type='client_credentials',
              scope=' '.join(self.scope)
          )
          headers = {'Content-Type': 'application/x-www-form-urlencoded'}
          kwargs = dict(
              data=data,
              headers=headers,
              proxies=self.proxies,
              timeout=self.timeout
          )
          url = self.router.oauth[self.environment]
          auth_response = requests.request('POST', url,
                                           **kwargs)
          try:
              auth_response.raise_for_status()
          except requests.exceptions.HTTPError:
              auth_response = auth_response.json()
              raise AuthenticationError(auth_response.get('error'))
          auth_response = auth_response.json()
          self.scope = auth_response.get('scope', '').split(' ')
          self.access_token = auth_response.get('access_token')
          self.refresh_token = auth_response.get('refresh_token')
          self._auth = RadarlyAuth(self.access_token)
          return None
  
      def refresh(self):
          """
          Refresh the access_token using the refresh_token as soon as the access
          token has expired. The auto-refresh behaviour can be ignored with
          the autorefresh attribute.
          """
          data = dict(
              client_id=self.client_id,
              client_secret=self.client_secret,
              grant_type='refresh_token',
              refresh_token=self.refresh_token,
          )
          headers = {
              'Content-Type': 'application/x-www-form-urlencoded'
          }
          url = self.router.oauth[self.environment]
          auth_response = requests.request('POST', url,
                                           data=data, headers=headers,
                                           proxies=self.proxies, timeout=self.timeout)
          auth_response = auth_response.json()
          self.access_token = auth_response.get('access_token')
          self.refresh_token = auth_response.get('refresh_token')
          self._auth = RadarlyAuth(self.access_token)
          self.last_refresh = datetime.now()
          return None
  