docs: def _perform_request(self, request, parser=None, parser_args=None, operation_context=None):
          '''
          Sends the request and return response. Catches HTTPError and hands it
          to error handler
          '''
          operation_context = operation_context or _OperationContext()
          retry_context = RetryContext()
          retry_context.is_emulated = self.is_emulated
  
          # Apply the appropriate host based on the location mode
          self._apply_host(request, operation_context, retry_context)
  
          # Apply common settings to the request
          _update_request(request, self._X_MS_VERSION, self._USER_AGENT_STRING)
          client_request_id_prefix = str.format("Client-Request-ID={0}", request.headers['x-ms-client-request-id'])
  
          while True:
              try:
                  try:
                      # Execute the request callback 
                      if self.request_callback:
                          self.request_callback(request)
  
                      # Add date and auth after the callback so date doesn't get too old and 
                      # authentication is still correct if signed headers are added in the request 
                      # callback. This also ensures retry policies with long back offs 
                      # will work as it resets the time sensitive headers.
                      _add_date_header(request)
                      self.authentication.sign_request(request)
  
                      # Set the request context
                      retry_context.request = request
  
                      # Log the request before it goes out
                      logger.info("%s Outgoing request: Method=%s, Path=%s, Query=%s, Headers=%s.",
                                  client_request_id_prefix,
                                  request.method,
                                  request.path,
                                  request.query,
                                  str(request.headers).replace('\n', ''))
  
                      # Perform the request
                      response = self._httpclient.perform_request(request)
  
                      # Execute the response callback
                      if self.response_callback:
                          self.response_callback(response)
  
                      # Set the response context
                      retry_context.response = response
  
                      # Log the response when it comes back
                      logger.info("%s Receiving Response: "
                                  "%s, HTTP Status Code=%s, Message=%s, Headers=%s.",
                                  client_request_id_prefix,
                                  self.extract_date_and_request_id(retry_context),
                                  response.status,
                                  response.message,
                                  str(request.headers).replace('\n', ''))
  
                      # Parse and wrap HTTP errors in AzureHttpError which inherits from AzureException
                      if response.status >= 300:
                          # This exception will be caught by the general error handler
                          # and raised as an azure http exception
                          _http_error_handler(
                              HTTPError(response.status, response.message, response.headers, response.body))
  
                      # Parse the response
                      if parser:
                          if parser_args:
                              args = [response]
                              args.extend(parser_args)
                              return parser(*args)
                          else:
                              return parser(response)
                      else:
                          return
                  except AzureException as ex:
                      retry_context.exception = ex
                      raise ex
                  except Exception as ex:
                      retry_context.exception = ex
                      if sys.version_info >= (3,):
                          # Automatic chaining in Python 3 means we keep the trace
                          raise AzureException(ex.args[0])
                      else:
                          # There isn't a good solution in 2 for keeping the stack trace 
                          # in general, or that will not result in an error in 3
                          # However, we can keep the previous error type and message
                          # TODO: In the future we will log the trace
                          msg = ""
                          if len(ex.args) > 0:
                              msg = ex.args[0]
                          raise AzureException('{}: {}'.format(ex.__class__.__name__, msg))
  
              except AzureException as ex:
                  # only parse the strings used for logging if logging is at least enabled for CRITICAL
                  if logger.isEnabledFor(logging.CRITICAL):
                      exception_str_in_one_line = str(ex).replace('\n', '')
                      status_code = retry_context.response.status if retry_context.response is not None else 'Unknown'
                      timestamp_and_request_id = self.extract_date_and_request_id(retry_context)
  
                  logger.info("%s Operation failed: checking if the operation should be retried. "
                              "Current retry count=%s, %s, HTTP status code=%s, Exception=%s.",
                              client_request_id_prefix,
                              retry_context.count if hasattr(retry_context, 'count') else 0,
                              timestamp_and_request_id,
                              status_code,
                              exception_str_in_one_line)
  
                  # Decryption failures (invalid objects, invalid algorithms, data unencrypted in strict mode, etc)
                  # will not be resolved with retries.
                  if str(ex) == _ERROR_DECRYPTION_FAILURE:
                      logger.error("%s Encountered decryption failure: this cannot be retried. "
                                   "%s, HTTP status code=%s, Exception=%s.",
                                   client_request_id_prefix,
                                   timestamp_and_request_id,
                                   status_code,
                                   exception_str_in_one_line)
                      raise ex
  
                  # Determine whether a retry should be performed and if so, how 
                  # long to wait before performing retry.
                  retry_interval = self.retry(retry_context)
                  if retry_interval is not None:
                      # Execute the callback
                      if self.retry_callback:
                          self.retry_callback(retry_context)
  
                      logger.info(
                          "%s Retry policy is allowing a retry: Retry count=%s, Interval=%s.",
                          client_request_id_prefix,
                          retry_context.count,
                          retry_interval)
  
                      # Sleep for the desired retry interval
                      sleep(retry_interval)
                  else:
                      logger.error("%s Retry policy did not allow for a retry: "
                                   "%s, HTTP status code=%s, Exception=%s.",
                                   client_request_id_prefix,
                                   timestamp_and_request_id,
                                   status_code,
                                   exception_str_in_one_line)
                      raise ex
              finally:
                  # If this is a location locked operation and the location is not set, 
                  # this is the first request of that operation. Set the location to 
                  # be used for subsequent requests in the operation.
                  if operation_context.location_lock and not operation_context.host_location:
                      # note: to cover the emulator scenario, the host_location is grabbed
                      # from request.host_locations(which includes the dev account name)
                      # instead of request.host(which at this point no longer includes the dev account name)
                      operation_context.host_location = {retry_context.location_mode: request.host_locations[retry_context.location_mode]}