docs: async def generic_request(self, class_, iter_lines=False, iter_content=False,
                                controller=None, parse_types=False, **kwargs):
          """Generic Space-Track query coroutine.
  
          The request class methods use this method internally; the public
          API is as follows:
  
          .. code-block:: python
  
              st.tle_publish(*args, **st)
              st.basicspacedata.tle_publish(*args, **st)
              st.file(*args, **st)
              st.fileshare.file(*args, **st)
              st.spephemeris.file(*args, **st)
  
          They resolve to the following calls respectively:
  
          .. code-block:: python
  
              st.generic_request('tle_publish', *args, **st)
              st.generic_request('tle_publish', *args, controller='basicspacedata', **st)
              st.generic_request('file', *args, **st)
              st.generic_request('file', *args, controller='fileshare', **st)
              st.generic_request('file', *args, controller='spephemeris', **st)
  
          Parameters:
              class_: Space-Track request class name
              iter_lines: Yield result line by line
              iter_content: Yield result in 100 KiB chunks.
              controller: Optionally specify request controller to use.
              parse_types: Parse string values in response according to type given
                  in predicate information, e.g. ``'2017-01-01'`` ->
                  ``datetime.date(2017, 1, 1)``.
              **kwargs: These keywords must match the predicate fields on
                  Space-Track. You may check valid keywords with the following
                  snippet:
  
                  .. code-block:: python
  
                      spacetrack = AsyncSpaceTrackClient(...)
                      await spacetrack.tle.get_predicates()
                      # or
                      await spacetrack.get_predicates('tle')
  
                  See :func:`~spacetrack.operators._stringify_predicate_value` for
                  which Python objects are converted appropriately.
  
          Yields:
              Lines—stripped of newline characters—if ``iter_lines=True``
  
          Yields:
              100 KiB chunks if ``iter_content=True``
  
          Returns:
              Parsed JSON object, unless ``format`` keyword argument is passed.
  
              .. warning::
  
                  Passing ``format='json'`` will return the JSON **unparsed**. Do
                  not set ``format`` if you want the parsed JSON object returned!
          """
          if iter_lines and iter_content:
              raise ValueError('iter_lines and iter_content cannot both be True')
  
          if 'format' in kwargs and parse_types:
              raise ValueError('parse_types can only be used if format is unset.')
  
          if controller is None:
              controller = self._find_controller(class_)
          else:
              classes = self.request_controllers.get(controller, None)
              if classes is None:
                  raise ValueError(
                      'Unknown request controller {!r}'.format(controller))
              if class_ not in classes:
                  raise ValueError(
                      'Unknown request class {!r} for controller {!r}'
                      .format(class_, controller))
  
          # Decode unicode unless class == download, including conversion of
          # CRLF newlines to LF.
          decode = (class_ != 'download')
          if not decode and iter_lines:
              error = (
                  'iter_lines disabled for binary data, since CRLF newlines '
                  'split over chunk boundaries would yield extra blank lines. '
                  'Use iter_content=True instead.')
              raise ValueError(error)
  
          await self.authenticate()
  
          url = ('{0}{1}/query/class/{2}'
                 .format(self.base_url, controller, class_))
  
          offline_check = (class_, controller) in self.offline_predicates
          valid_fields = {p.name for p in self.rest_predicates}
          predicates = None
  
          if not offline_check:
              predicates = await self.get_predicates(class_)
              predicate_fields = {p.name for p in predicates}
              valid_fields = predicate_fields | {p.name for p in self.rest_predicates}
          else:
              valid_fields |= self.offline_predicates[(class_, controller)]
  
          for key, value in kwargs.items():
              if key not in valid_fields:
                  raise TypeError(
                      "'{class_}' got an unexpected argument '{key}'"
                      .format(class_=class_, key=key))
  
              value = _stringify_predicate_value(value)
  
              url += '/{key}/{value}'.format(key=key, value=value)
  
          logger.debug(url)
  
          resp = await self._ratelimited_get(url)
  
          await _raise_for_status(resp)
  
          if iter_lines:
              return _AsyncLineIterator(resp, decode_unicode=decode)
          elif iter_content:
              return _AsyncChunkIterator(resp, decode_unicode=decode)
          else:
              # If format is specified, return that format unparsed. Otherwise,
              # parse the default JSON response.
              if 'format' in kwargs:
                  if decode:
                      # Replace CRLF newlines with LF, Python will handle platform
                      # specific newlines if written to file.
                      data = await resp.text()
                      data = data.replace('\r', '')
                  else:
                      data = await resp.read()
                  return data
              else:
                  data = await resp.json()
  
                  if predicates is None or not parse_types:
                      return data
                  else:
                      return self._parse_types(data, predicates)