text: ################################################################################
  #
  # Copyright (c) 2019, the Perspective Authors.
  #
  # This file is part of the Perspective library, distributed under the terms of
  # the Apache License 2.0.  The full license can be found in the LICENSE file.
  #
  
  import tornado
  
  from functools import partial
  from .dispatch import async_queue, subscribe, unsubscribe
  from .view_api import view as make_view
  
  
  def table(client, data, name, index=None, limit=None):
      """Create a Perspective `Table` by posting a message to a Perspective
      server implementation through `client`, returning a `PerspectiveTableProxy`
      object whose API is entirely async and must be called with `await` or
      in a `yield`-based generator."""
      options = {}
  
      if index:
          options["index"] = index
      elif limit:
          options["limit"] = limit
  
      msg = {"cmd": "table", "name": name, "args": [data], "options": options}
  
      future = tornado.concurrent.Future()
      client.post(msg, future)
      return future
  
  
  class PerspectiveTableProxy(object):
      def __init__(self, client, name):
          """A proxy for a Perspective `Table` object elsewhere, i.e. on a remote
          server accessible through a Websocket.
  
          All public API methods on this proxy are async, and must be called
          with `await` or a `yield`-based coroutine.
  
          Args:
              client (:obj:`PerspectiveClient`): A `PerspectiveClient` that is
                  set up to send messages to a Perspective server implementation
                  elsewhere.
  
              name (:obj:`str`): a `str` name for the Table. Automatically
                  generated if using the `table` function defined above.
          """
          self._client = client
          self._name = name
          self._async_queue = partial(async_queue, self._client, self._name)
          self._subscribe = partial(subscribe, self._client, self._name)
          self._unsubscribe = partial(unsubscribe, self._client, self._name)
  
      def make_port(self):
          return self._async_queue("make_port", "table_method")
  
      def remove_port(self):
          return self._async_queue("remove_port", "table_method")
  
      def get_index(self):
          return self._async_queue("get_index", "table_method")
  
      def get_limit(self):
          return self._async_queue("get_limit", "table_method")
  
      def clear(self):
          return self._async_queue("clear", "table_method")
  
      def replace(self, data):
          return self._async_queue("replace", "table_method", data)
  
      def size(self):
          return self._async_queue("size", "table_method")
  
      def schema(self, as_string=False):
          return self._async_queue("schema", "table_method", as_string=as_string)
  
      def expression_schema(self, expressions, **kwargs):
          return self._async_queue(
              "expression_schema", "table_method", expressions, **kwargs
          )
  
      def columns(self):
          return self._async_queue("columns", "table_method")
  
      def is_valid_filter(self, filter):
          return self._async_queue("is_valid_filter", "table_method", filter)
  
      def on_delete(self, callback):
          return self._subscribe("on_delete", "table_method", callback)
  
      def remove_delete(self, callback):
          return self._unsubscribe("remove_delete", "table_method", callback)
  
      def delete(self):
          return self._async_queue("delete", "table_method")
  
      def view(
          self,
          columns=None,
          row_pivots=None,
          column_pivots=None,
          aggregates=None,
          sort=None,
          filter=None,
          expressions=None,
      ):
          return make_view(
              self._client,
              self._name,
              columns,
              row_pivots,
              column_pivots,
              aggregates,
              sort,
              filter,
              expressions,
          )
  
      def update(self, data, port_id=0):
          msg = {
              "name": self._name,
              "cmd": "table_method",
              "method": "update",
              "args": [data, {"port_id": port_id}],
              "subscribe": False,
          }
          self._client.post(msg)
  
      def remove(self, pkeys, port_id=0):
          msg = {
              "name": self._name,
              "cmd": "table_method",
              "method": "remove",
              "args": [pkeys, {"port_id": port_id}],
              "subscribe": False,
          }
          self._client.post(msg)
  