docs: def _query(self, method, path, data=None, page=False, retry=0):
          """
          Fetch an object from the Graph API and parse the output, returning a tuple where the first item
          is the object yielded by the Graph API and the second is the URL for the next page of results, or
          ``None`` if results have been exhausted.
  
          :param method: A string describing the HTTP method.
          :param path: A string describing the object in the Graph API.
          :param data: A dictionary of HTTP GET parameters (for GET requests) or POST data (for POST requests).
          :param page: A boolean describing whether to return an iterator that iterates over each page of results.
          :param retry: An integer describing how many times the request may be retried.
          """
  
          if(data):
              data = dict(
                  (k.replace('_sqbro_', '['), v) for k, v in data.items())
              data = dict(
                  (k.replace('_sqbrc_', ']'), v) for k, v in data.items())
              data = dict(
                  (k.replace('__', ':'), v) for k, v in data.items())
          data = data or {}
  
          def load(method, url, data):
              for key in data:
                  value = data[key]
  
                  if isinstance(value, (list, dict, set)):
                      data[key] = json.dumps(value)
  
              try:
                  if method in ['GET', 'DELETE']:
                      response = self.session.request(
                          method, url, params=data, allow_redirects=True,
                          verify=self.verify_ssl_certificate, timeout=self.timeout
                      )
  
                  if method in ['POST', 'PUT']:
                      files = {}
  
                      for key in data:
                          if hasattr(data[key], 'read'):
                              files[key] = data[key]
  
                      for key in files:
                          data.pop(key)
  
                      response = self.session.request(
                          method, url, data=data, files=files,
                          verify=self.verify_ssl_certificate, timeout=self.timeout
                      )
  
                  if 500 <= response.status_code < 600:
                      # Facebook 5XX errors usually come with helpful messages
                      # as a JSON object describing the problem with the request.
                      # If this is the case, an error will be raised and we just
                      # need to re-raise it. This is most likely to happen
                      # with the Ads API.
                      # This will raise an exception if a JSON-like error object
                      # comes in the response.
                      self._parse(response.content)
                      # If Facebook does not provide any JSON-formatted error
                      # but just a plain-text, useless error, we'll just inform
                      # about a Facebook Internal errror occurred.
                      raise FacebookError(
                          'Internal Facebook error occurred',
                          response.status_code
                      )
  
              except requests.RequestException as exception:
                  raise HTTPError(exception)
  
              result = self._parse(response.content)
              if isinstance(result, dict):
                  result['headers'] = response.headers
  
              try:
                  next_url = result['paging']['next']
              except (KeyError, TypeError):
                  next_url = None
  
              return result, next_url
  
          def load_with_retry(method, url, data):
              remaining_retries = retry
              while True:
                  try:
                      return load(method, url, data)
                  except FacepyError as e:
                      log.warn("Exception on %s: %s, retries remaining: %s",
                               url,
                               e,
                               remaining_retries,
                               )
                      if remaining_retries > 0:
                          remaining_retries -= 1
                      else:
                          raise
  
          def paginate(method, url, data):
              while url:
                  result, url = load_with_retry(method, url, data)
  
                  # Reset pagination parameters.
                  for key in ['offset', 'until', 'since']:
                      if key in data:
                          del data[key]
  
                  yield result
  
          # Convert option lists to comma-separated values.
          for key in data:
              if isinstance(data[key], (list, set, tuple)) and all([isinstance(item, six.string_types) for item in data[key]]):
                  data[key] = ','.join(data[key])
  
          # Support absolute paths too
          if not path.startswith('/'):
              if six.PY2:
                  path = '/' + six.text_type(path.decode('utf-8'))
              else:
                  path = '/' + path
  
          url = self._get_url(path)
  
          if self.oauth_token:
              data['access_token'] = self.oauth_token
  
          if self.appsecret and self.oauth_token:
              data['appsecret_proof'] = self._generate_appsecret_proof()
  
          if page:
              return paginate(method, url, data)
          else:
              return load_with_retry(method, url, data)[0]