docs: def geocode(self, pq, waterfall=None, force_stats_logging=False):
          """
          :arg PlaceQuery pq:  PlaceQuery object (required).
          :arg bool waterfall: Boolean set to True if all geocoders listed should
                               be used to find results, instead of stopping after
                               the first geocoding service with valid candidates
                               (defaults to self.waterfall).
          :arg bool force_stats_logging: Raise exception if stats logging fails (default False).
          :returns: Returns a dictionary including:
                     * candidates - list of Candidate objects
                     * upstream_response_info - list of UpstreamResponseInfo objects
          """
  
          waterfall = self.waterfall if waterfall is None else waterfall
          if type(pq) in (str, str):
              pq = PlaceQuery(pq)
          processed_pq = copy.copy(pq)
  
          for p in self._preprocessors:  # apply universal address preprocessing
              processed_pq = p.process(processed_pq)
  
          upstream_response_info_list = []
          processed_candidates = []
          for gs in self._sources:  # iterate through each GeocodeService
              candidates, upstream_response_info = gs.geocode(processed_pq)
              if upstream_response_info is not None:
                  upstream_response_info_list.append(upstream_response_info)
              processed_candidates += candidates  # merge lists
              if waterfall is False and len(processed_candidates) > 0:
                  break  # if >= 1 good candidate, don't go to next geocoder
  
          for p in self._postprocessors:  # apply univ. candidate postprocessing
              if processed_candidates == []:
                  break  # avoid post-processing empty list
              processed_candidates = p.process(processed_candidates)
  
          result = dict(candidates=processed_candidates,
                        upstream_response_info=upstream_response_info_list)
          stats_dict = self.convert_geocode_result_to_nested_dicts(result)
          stats_dict = dict(stats_dict, original_pq=pq.__dict__)
          try:
              stats_logger.info(stats_dict)
          except Exception as exception:
              logger.error('Encountered exception while logging stats %s:\n%s', stats_dict, exception)
              if force_stats_logging:
                  raise exception
          return result