docs: def lookup(self, inc_raw=False, retry_count=3, asn_data=None, depth=0,
                 excluded_entities=None, response=None, bootstrap=False,
                 rate_limit_timeout=120):
          """
          The function for retrieving and parsing information for an IP
          address via RDAP (HTTP).
  
          Args:
              inc_raw (:obj:`bool`, optional): Whether to include the raw
                  results in the returned dictionary. Defaults to False.
              retry_count (:obj:`int`): The number of times to retry in case
                  socket errors, timeouts, connection resets, etc. are
                  encountered. Defaults to 3.
              asn_data (:obj:`dict`): Result from
                  :obj:`ipwhois.asn.IPASN.lookup`. Optional if the bootstrap
                  parameter is True.
              depth (:obj:`int`): How many levels deep to run queries when
                  additional referenced objects are found. Defaults to 0.
              excluded_entities (:obj:`list`): Entity handles to not perform
                  lookups. Defaults to None.
              response (:obj:`str`): Optional response object, this bypasses the
                  RDAP lookup.
              bootstrap (:obj:`bool`): If True, performs lookups via ARIN
                  bootstrap rather than lookups based on ASN data. Defaults to
                  False.
              rate_limit_timeout (:obj:`int`): The number of seconds to wait
                  before retrying when a rate limit notice is returned via
                  rdap+json. Defaults to 120.
  
          Returns:
              dict: The IP RDAP lookup results
  
              ::
  
                  {
                      'query' (str) - The IP address
                      'entities' (list) - Entity handles referred by the top
                          level query.
                      'network' (dict) - Network information which consists of
                          the fields listed in the ipwhois.rdap._RDAPNetwork
                          dict.
                      'objects' (dict) - Mapping of entity handle->entity dict
                          which consists of the fields listed in the
                          ipwhois.rdap._RDAPEntity dict. The raw result is
                          included for each object if the inc_raw parameter
                          is True.
                  }
          """
  
          if not excluded_entities:
  
              excluded_entities = []
  
          # Create the return dictionary.
          results = {
              'query': self._net.address_str,
              'network': None,
              'entities': None,
              'objects': None,
              'raw': None
          }
  
          if bootstrap:
  
              ip_url = '{0}/ip/{1}'.format(BOOTSTRAP_URL, self._net.address_str)
  
          else:
  
              ip_url = str(RIR_RDAP[asn_data['asn_registry']]['ip_url']).format(
                  self._net.address_str)
  
          # Only fetch the response if we haven't already.
          if response is None:
  
              log.debug('Response not given, perform RDAP lookup for {0}'.format(
                  ip_url))
  
              # Retrieve the whois data.
              response = self._net.get_http_json(
                  url=ip_url, retry_count=retry_count,
                  rate_limit_timeout=rate_limit_timeout
              )
  
          if inc_raw:
  
              results['raw'] = response
  
          log.debug('Parsing RDAP network object')
          result_net = _RDAPNetwork(response)
          result_net.parse()
          results['network'] = result_net.vars
          results['entities'] = []
          results['objects'] = {}
          roles = {}
  
          # Iterate through and parse the root level entities.
          log.debug('Parsing RDAP root level entities')
          try:
  
              for ent in response['entities']:
  
                  if ent['handle'] not in [results['entities'],
                                           excluded_entities]:
  
                      result_ent = _RDAPEntity(ent)
                      result_ent.parse()
  
                      results['objects'][ent['handle']] = result_ent.vars
  
                      results['entities'].append(ent['handle'])
  
                      try:
  
                          for tmp in ent['entities']:
  
                              roles[tmp['handle']] = tmp['roles']
  
                      except KeyError:
  
                          pass
  
          except KeyError:
  
              pass
  
          # Iterate through to the defined depth, retrieving and parsing all
          # unique entities.
          temp_objects = results['objects']
  
          if depth > 0 and len(temp_objects) > 0:
  
              log.debug('Parsing RDAP sub-entities to depth: {0}'.format(str(
                  depth)))
  
          while depth > 0 and len(temp_objects) > 0:
  
              new_objects = {}
              for obj in temp_objects.values():
  
                  try:
  
                      for ent in obj['entities']:
  
                          if ent not in (list(results['objects'].keys()) +
                                         list(new_objects.keys()) +
                                         excluded_entities):
  
                              if bootstrap:
                                  entity_url = '{0}/entity/{1}'.format(
                                      BOOTSTRAP_URL, ent)
                              else:
                                  tmp_reg = asn_data['asn_registry']
                                  entity_url = RIR_RDAP[tmp_reg]['entity_url']
                                  entity_url = str(entity_url).format(ent)
  
                              try:
  
                                  # RDAP entity query
                                  response = self._net.get_http_json(
                                      url=entity_url, retry_count=retry_count,
                                      rate_limit_timeout=rate_limit_timeout
                                  )
  
                                  # Parse the entity
                                  result_ent = _RDAPEntity(response)
                                  result_ent.parse()
                                  new_objects[ent] = result_ent.vars
  
                                  new_objects[ent]['roles'] = None
                                  try:
  
                                      new_objects[ent]['roles'] = roles[ent]
  
                                  except KeyError:  # pragma: no cover
  
                                      pass
  
                                  try:
  
                                      for tmp in response['entities']:
  
                                          if tmp['handle'] not in roles:
  
                                              roles[tmp['handle']] = tmp['roles']
  
                                  except (IndexError, KeyError):
  
                                      pass
  
                                  if inc_raw:
  
                                      new_objects[ent]['raw'] = response
  
                              except (HTTPLookupError, InvalidEntityObject):
  
                                  pass
  
                  except TypeError:
  
                      pass
  
              # Update the result objects, and set the new temp object list to
              # iterate for the next depth of entities.
              results['objects'].update(new_objects)
              temp_objects = new_objects
              depth -= 1
  
          return results