text: import abc
  from time import sleep
  
  from office365.runtime.client_request_exception import ClientRequestException
  from office365.runtime.compat import is_absolute_url
  from office365.runtime.http.request_options import RequestOptions
  from office365.runtime.queries.read_entity_query import ReadEntityQuery
  
  
  class ClientRuntimeContext(object):
  
      def build_request(self):
          return self.pending_request().build_request()
  
      def build_single_request(self, query):
          """
  
          :type: office365.runtime.queries.client_query.ClientQuery
          """
          return self.pending_request().build_single_request(query)
  
      def execute_query_retry(self, max_retry=5, timeout_secs=5, success_callback=None, failure_callback=None,
                              exceptions=(ClientRequestException,)):
          """
          Executes the current set of data retrieval queries and method invocations and retries it if needed.
  
          :param int max_retry: Number of times to retry the request
          :param int timeout_secs: Seconds to wait before retrying the request.
          :param (office365.runtime.client_object.ClientObject)-> None success_callback:
          :param (int, requests.exceptions.RequestException)-> None failure_callback:
          :param exceptions: tuple of exceptions that we retry
          """
  
          for retry in range(1, max_retry):
              try:
                  self.execute_query()
                  if callable(success_callback):
                      success_callback(self.current_query.return_type)
                  break
              except exceptions as e:
                  self.add_query(self.current_query, True)
                  sleep(timeout_secs)
                  if callable(failure_callback):
                      failure_callback(retry, e)
  
      @abc.abstractmethod
      def pending_request(self):
          """
          :rtype: office365.runtime.client_request.ClientRequest
          """
          pass
  
      @abc.abstractmethod
      def service_root_url(self):
          pass
  
      @abc.abstractmethod
      def authenticate_request(self, request):
          """
          :type request: office365.runtime.http.request_options.RequestOptions
          """
          pass
  
      def load(self, client_object, properties_to_retrieve=None):
          """Prepare retrieval query
  
          :type properties_to_retrieve: list[str] or None
          :type client_object: office365.runtime.client_object.ClientObject
          """
          qry = ReadEntityQuery(client_object, properties_to_retrieve)
          self.add_query(qry)
          return qry
  
      def before_execute(self, action, once=True, *args, **kwargs):
          """
          Attach an event handler which is triggered before request is submitted to server
  
          :param (RequestOptions, any) -> None action:
          :param bool once:
          :return: None
          """
  
          def _process_request(request):
              if once:
                  self.pending_request().beforeExecute -= _process_request
              action(request, *args, **kwargs)
  
          self.pending_request().beforeExecute += _process_request
  
      def after_query_execute(self, query, action, *args, **kwargs):
          """
          Attach an event handler which is triggered after query is submitted to server
  
          :type query: office365.runtime.queries.client_query.ClientQuery
          :type action: (Response, *args, **kwargs) -> None
          :return: None
          """
  
          def _process_response(resp):
              """
              :type resp: requests.Response
              """
              if self.current_query.id == query.id:
                  action(*args, **kwargs)
                  self.pending_request().afterExecute -= _process_response
  
          self.pending_request().afterExecute += _process_response
  
      def after_execute(self, action, once=True, *args, **kwargs):
          """
          Attach an event handler which is triggered after request is submitted to server
  
          :param (RequestOptions, *args, **kwargs) -> None action:
          :param bool once:
          :return: None
          """
  
          def _process_response(response):
              if once:
                  self.pending_request().afterExecute -= _process_response
              action(response, *args, **kwargs)
  
          self.pending_request().afterExecute += _process_response
  
      def execute_request_direct(self, request):
          """
          :type request: office365.runtime.http.request_options.RequestOptions or str
          """
          return self.pending_request().execute_request_direct(self._normalize_request(request))
  
      def execute_query(self):
          self.pending_request().execute_query()
  
      def add_query(self, query, execute_first=False, set_as_current=True):
          """
          :type query: office365.runtime.queries.client_query.ClientQuery
          :type execute_first: bool
          :type set_as_current: bool
          """
          self.pending_request().add_query(query, execute_first, set_as_current)
  
      def clear_queries(self):
          self.pending_request().queries.clear()
  
      @property
      def current_query(self):
          """
          :rtype: office365.runtime.queries.client_query.ClientQuery
          """
          return self.pending_request().current_query
  
      def _normalize_request(self, request):
          """
          :type request: office365.runtime.http.request_options.RequestOptions or str
          """
          if not isinstance(request, RequestOptions):
              request = RequestOptions(request)
  
          if not is_absolute_url(request.url):
              url_parts = [self.service_root_url()]
              if not request.url.startswith("/"):
                  url_parts.append("/")
              url_parts.append(request.url)
              request.url = "".join(url_parts)
          return request
  