text: from .Response import *
  
  
  class Requester:
      def __init__(self, link):
          self.link = link
          self.request_manager = RequestManager()
          self.subscription_manager = RemoteSubscriptionManager()
          self.rid = 1
          self.sid = 0
  
      def get_next_rid(self):
          """
          Get the next rid in the sequence. Initially starts from 1.
          :return: Next rid in sequence.
          """
          r = self.rid
          self.rid += 1
          return r
  
      def get_next_sid(self):
          """
          Get the next sid in the sequence. Initially starts from 0.
          :return: Next sid in the sequence.
          """
          s = self.sid
          self.sid += 1
          return s
  
      def list(self, path, callback):
          """
          List a remote node.
          :param path: Request path.
          :param callback: Response callback.
          """
          rid = self.get_next_rid()
          self.link.wsp.sendMessage({
              "requests": [
                  {
                      "rid": rid,
                      "method": "list",
                      "path": path
                  }
              ]
          }, self)
          self.request_manager.start_request(rid, "list", callback, metadata={
              "path": path
          })
  
      def set(self, path, value, permit=None, callback=None):
          """
          Set a remote value.
          :param path: Path of value.
          :param value: Value to set.
          :param permit: Maximum permission of set.
          :param callback: Response callback.
          """
          rid = self.get_next_rid()
          i = {
              "rid": rid,
              "method": "set",
              "path": path,
              "value": value
          }
          if permit is not None:
              i["permit"] = permit
          self.link.wsp.sendMessage({
              "requests": [
                  i
              ]
          })
          if callback:
              self.request_manager.start_request(rid, "set", callback)
  
      def remove(self, path, callback=None):
          """
          Remove a remote value.
          :param path: Path of value.
          :param callback: Response callback.
          """
          rid = self.get_next_rid()
          self.link.wsp.sendMessage({
              "rid": rid,
              "method": "remove",
              "path": path
          })
          if callback:
              self.request_manager.start_request(rid, "remove", callback)
  
      def invoke(self, path, permit=None, params=None, callback=None):
          """
          Invoke a remote method.
          :param path: Path of node.
          :param permit: Maximum permission of invoke.
          :param params: Parameters of invoke.
          :param callback: Response callback.
          """
          rid = self.get_next_rid()
          i = {
              "rid": rid,
              "method": "invoke",
              "path": path
          }
          if permit is not None:
              i["permit"] = permit
          if params is not None:
              i["params"] = params
          self.link.wsp.sendMessage({
              "requests": [
                  i
              ]
          })
          if callback:
              self.request_manager.start_request(rid, "invoke", callback)
  
      def subscribe(self, path, callback, qos=0):
          """
          Subscribe to a remote Node.
          :param path: Path of Node.
          :param callback: Response callback.
          :param qos: Quality of Service.
          """
          if type(path) is not str:
              raise ValueError("Unsupported path type.")
          rid = self.get_next_rid()
          sid = self.get_next_sid()
          self.subscription_manager.subscribe(sid, path, callback, qos)
          self.link.wsp.sendMessage({
              "requests": [
                  {
                      "rid": rid,
                      "method": "subscribe",
                      "paths": [
                          {
                              "path": path,
                              "sid": sid,
                              "qos": qos
                          }
                      ]
                  }
              ]
          })
  
      def unsubscribe(self, path):
          """
          Unsubscribe to a remote Node.
          :param path: Path of Node.
          """
          if type(path) is str:
              sid = self.subscription_manager.get_sid_by_path(path)
              self.subscription_manager.unsubscribe(sid)
              rid = self.get_next_rid()
              self.link.wsp.sendMessage({
                  "requests": [
                      {
                          "rid": rid,
                          "method": "unsubscribe",
                          "sids": [
                              sid
                          ]
                      }
                  ]
              })
          else:
              raise ValueError("Unsupported path type.")
  
      def close(self, rid):
          """
          Close a stream.
          :param rid: ID of request.
          """
          self.link.wsp.sendMessage({
              "requests": [
                  {
                      "rid": rid,
                      "method": "close"
                  }
              ]
          })
  
  
  class RequestManager:
      """
      Manages outgoing requests and callbacks.
      """
  
      def __init__(self):
          """
          Constructor of RequestManager.
          """
          self.requests = {}
  
      def start_request(self, rid, type, callback, metadata=None):
          """
          Start a Request.
          :param rid: RID of Request.
          :param type: Type of Request.
          :param callback: Callback to invoke.
          :param metadata: Metadata for request.
          """
          if not metadata:
              metadata = {}
          self.requests[rid] = {
              "type": type,
              "callback": callback,
              "metadata": metadata
          }
  
      def stop_request(self, rid):
          """
          Stop a Request.
          :param rid: RID of Request.
          """
          del self.requests[rid]
  
      def invoke_request(self, rid, data):
          """
          Invoke a Request.
          :param rid: RID of Request.
          :param data: Data of Request.
          """
          request = self.requests[rid]
          response = data
          if request["type"] == "list":
              response = ListResponse(data, request["metadata"]["path"])
          request["callback"](response)
  
  
  class RemoteSubscriptionManager:
      def __init__(self):
          self.subscriptions = {}
  
      def subscribe(self, sid, path, callback, qos=0):
          """
          Subscribe to a Node.
          :param sid: Subscription ID.
          :param path: Path of Node.
          :param callback: Callback to run with value.
          :param qos: Quality of Service.
          """
          if not hasattr(callback, "__call__"):
              raise ValueError("Passed callback is not callable.")
          elif sid in self.subscriptions:
              raise ValueError("Sid %s is already in use." % sid)
          self.subscriptions[sid] = {
              "path": path,
              "callback": callback,
              "qos": qos
          }
  
      def unsubscribe(self, sid):
          """
          Unsubscribe from a Node.
          :param sid: Subscription ID.
          """
          if sid not in self.subscriptions:
              raise ValueError("Sid %s not in use." % sid)
          del self.subscriptions[sid]
  
      def get_sid_by_path(self, path):
          """
          Get a Subscription ID by path.
          :param path: Path of Node.
          :return: Subscription ID.
          """
          for sid in self.subscriptions:
              meta = self.subscriptions[sid]
              if meta["path"] == path:
                  return sid
  
      def run_callback(self, sid, value, time, count=0, sum=0, min=0, max=0):
          """
          Run a subscription's callback.
          :param sid: Subscription ID.
          :param value: Value.
          :param time: Updated at time.
          :param count: Number of values merged.
          :param sum: Sum of values merged.
          :param min: Minimum value of values merged.
          :param max: Maximum value of values merged.
          """
          self.subscriptions[sid]["callback"]((value, time, count, sum, min, max))
  