示例#1
0
class IPythonSession(object):
    def __init__(self):
        self.comm = None

        folder = os.path.dirname(__file__)
        with open(os.path.join(folder, "../js", "initIPython.js"), "r") as fd:
            initIPython = fd.read()

        display_javascript(Javascript(initIPython))

        css = """
            <style>
                div.output_area img, div.output_area svg {
                    max-width: 100%;
                    height: 100%;
                }
            </style>
        """
        display_html(HTML(css))

        time.sleep(0.5)
        self.comm = Comm(target_name='nvd3_stat', data={'event': 'open'})

    def registerFunction(self, funcName, funcBody):
        # Zeppelin only
        pass

    def call(self, funcName, args, delay):
        # Explicitely open comm channel in case notebook has been reloaded
        self.comm.open()
        self.comm.send({"funcName": funcName, "args": args, "delay": delay})
示例#2
0
    def add_widget(self, widget_id, client_id, widget_type, comm_target):
        """Add a widget to manager.
        
        # Returns:
            
        """
        comm = self.find_comm(
            comm_target=comm_target)  # Try to find a comm object
        if not comm:  # Create comm if does not exist
            comm = Comm(target_name=comm_target)
            self._comms[widget_id] = comm

            @comm.on_msg
            def handle_msg(msg):
                comm_id = msg["content"]["comm_id"]
                data = msg["content"]["data"]
                nonlocal self
                widget = self.find_widget_by_comm_id(comm_id)
                self.last_active = widget
                widget.msg_data = data
                widget.parse_data(data)
                if data == "dispose":
                    widget.isDisposed = True
                self.callback_manager.run(comm_id, data)

            @comm.on_close
            def handle_close(msg):
                comm_id = msg["content"]["comm_id"]
                nonlocal self
                widget = self.find_widget_by_comm_id(comm_id)
                widget.commOpen = False

        if widget_id not in self.widgets:
            self.widgets[widget_id] = Widget(
                widget_type=widget_type,
                widget_id=widget_id,
                client_id=client_id,
                model=None,
                comm=comm,
                isDisposed=False,
                msg_data=None,
            )
        self.last_active = self.widgets[widget_id]

        # make sure that comm is open
        comm.open()
示例#3
0
class WWTLabApplication(BaseWWTWidget):
    """
    A handle the WWT JupyterLab application.

    While other parts of pywwt create "widgets", bound to variables running
    inside Python notebooks, this class represents a connection to the
    standalone "application", which exists in JupyterLab independently of any
    one specific notebook. The Python API is the same, it's just that the JSON
    messages we send are routed to the separate application rather than our own
    iframe.

    """
    _comm = None
    _controls = None

    # View state that gets synchronized back to us. This is the same scheme as
    # the widget, just with manual synchronization over our comm to the viewer
    # app.
    _raRad = 0.0
    _decRad = 0.0
    _fovDeg = 60.0
    _engineTime = Time('2017-03-09T12:30:00', format='isot')
    _systemTime = Time('2017-03-09T12:30:00', format='isot')
    _timeRate = 1.0

    def __init__(self):
        self._comm = Comm(target_name='@wwtelescope/jupyterlab:research', data={})
        self._comm.on_msg(self._on_message_received)
        self._comm.open()
        self._send_msg(event='trigger')  # get bidirectional updates flowing

        BaseWWTWidget.__init__(self)

    def _send_msg(self, **kwargs):
        self._comm.send(kwargs)

    def _on_message_received(self, msg):
        payload = msg['content']['data']
        if payload['type'] != 'wwt_view_state':
            return

        try:
            self._raRad = float(payload['raRad'])
            self._decRad = float(payload['decRad'])
            self._fovDeg = float(payload['fovDeg'])
            self._engineTime = Time(payload['engineClockISOT'], format='isot')
            self._systemTime = Time(payload['systemClockISOT'], format='isot')
            self._timeRate = float(payload['engineClockRateFactor'])
        except ValueError:
            pass  # report a warning somehow?

    def _serve_file(self, filename, extension=''):
        return serve_file(filename, extension=extension)

    def _get_view_data(self, field):
        if field == 'ra':
            return self._raRad * R2H
        elif field == 'dec':
            return self._decRad * R2D
        elif field == 'fov':
            return self._fovDeg
        elif field == 'datetime':
            engine_delta = self._timeRate * (Time.now() - self._systemTime)
            return self._engineTime + engine_delta
        else:
            raise ValueError('internal problem: unexpected "field" value')

    def _create_image_layer(self, **kwargs):
        """Returns a specialized subclass of ImageLayer that has some extra hooks for
        creating UI control points.

        """
        return JupyterImageLayer(parent=self, **kwargs)

    @property
    def layer_controls(self):
        if self._controls is None:
            opacity_slider = widgets.FloatSlider(value=self.foreground_opacity,
                                                 min=0, max=1, readout=False)
            foreground_menu = widgets.Dropdown(options=self.available_layers,
                                               value=self.foreground)
            background_menu = widgets.Dropdown(options=self.available_layers,
                                               value=self.background)
            link((opacity_slider, 'value'), (self, 'foreground_opacity'))
            link((foreground_menu, 'value'), (self, 'foreground'))
            link((background_menu, 'value'), (self, 'background'))
            self._controls = widgets.HBox([background_menu, opacity_slider, foreground_menu])
        return self._controls
示例#4
0
class BeakerX:

    def __init__(self):
        BeakerX.pandas_display_table()
        self._comm = None
        self._queue = Queue()
        self._server = BeakerxZMQServer(self._queue)
        self._url = self._server.url

    @staticmethod
    def pandas_display_default():
        pandas.DataFrame._ipython_display_ = None

    @staticmethod
    def pandas_display_table():
        pandas.DataFrame._ipython_display_ = TableDisplayWrapper()

    def set4(self, var, val, unset, sync):
        args = {'name': var, 'sync': sync}
        if not unset:
            val = transform(val)
            args['value'] = json.dumps(val, cls=DataFrameEncoder)
            state = {'state': args}
        if self._comm is None:
            self.init_autotranslation_comm()
        self._comm.send(data=state)

    def init_autotranslation_comm(self):
        self._comm = Comm(target_name='beakerx.autotranslation')
        self._comm.open()

    def get(self, var):
        result = autotranslation_get(var)
        if result == 'undefined':
            raise NameError('name \'' + var + '\' is not defined on the beakerx object')
        return transformBack(json.loads(result))

    def set_session(self, id):
        self.session_id = id

    def register_output(self):
        ip = IPython.InteractiveShell.instance()
        ip.display_formatter.formatters['application/json'] = MyJSONFormatter(parent=ip.display_formatter)

    def set(self, var, val):
        autotranslation_update(var, val)
        return self.set4(var, val, False, True)

    def unset(self, var):
        return self.set4(var, None, True, True)

    def isDefined(self, var):
        return autotranslation_get(var) != 'undefined'

    def createOutputContainer(self):
        return OutputContainer()

    def showProgressUpdate(self):
        return "WARNING: python3 language plugin does not support progress updates"

    def evaluate(self, filter):
        args = {'filter': filter, 'session': self.session_id}
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/evaluate',
                                     urllib.parse.urlencode(args).encode('utf8'))
        conn = self._beaker_url_opener.open(req)
        result = json.loads(conn.read().decode())
        return transformBack(result)

    def evaluateCode(self, evaluator, code):
        args = {'evaluator': evaluator, 'code': code, 'session': self.session_id}
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/evaluateCode',
                                     urllib.parse.urlencode(args).encode('utf8'))
        conn = self._beaker_url_opener.open(req)
        result = json.loads(conn.read().decode())
        return transformBack(result)

    def showStatus(self, msg):
        args = {'msg': msg, 'session': self.session_id}
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/showStatus',
                                     urllib.parse.urlencode(args).encode('utf8'))
        conn = self._beaker_url_opener.open(req)
        result = conn.read()
        return result == "true"

    def clearStatus(self, msg):
        args = {'msg': msg, 'session': self.session_id}
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/clearStatus',
                                     urllib.parse.urlencode(args).encode('utf8'))
        conn = self._beaker_url_opener.open(req)
        result = conn.read()
        return result == "true"

    def showTransientStatus(self, msg):
        args = {'msg': msg, 'session': self.session_id}
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/showTransientStatus',
                                     urllib.parse.urlencode(args).encode('utf8'))
        conn = self._beaker_url_opener.open(req)
        result = conn.read()
        return result == "true"

    def getEvaluators(self):
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/getEvaluators?' +
                                     urllib.parse.urlencode({
                                         'session': self.session_id}))
        conn = self._beaker_url_opener.open(req)
        result = json.loads(conn.read().decode())
        return transformBack(result)

    def getVersion(self):
        req = urllib.request.Request(
            self.core_url + '/rest/util/version?' + urllib.parse.urlencode({'session': self.session_id}))
        conn = self._beaker_url_opener.open(req)
        return transformBack(conn.read().decode())

    def getVersionNumber(self):
        req = urllib.request.Request(
            self.core_url + '/rest/util/getVersionInfo?' + urllib.parse.urlencode({'session': self.session_id}))
        conn = self._beaker_url_opener.open(req)
        result = json.loads(conn.read().decode())
        return transformBack(result['version'])

    def getCodeCells(self, filter):
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/getCodeCells?' +
                                     urllib.parse.urlencode({
                                         'filter': filter}))
        conn = self._beaker_url_opener.open(req)
        result = json.loads(conn.read().decode())
        return transformBack(result)

    def setCodeCellBody(self, name, body):
        args = {'name': name, 'body': body, 'session': self.session_id}
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/setCodeCellBody',
                                     urllib.parse.urlencode(args).encode('utf8'))
        conn = self._beaker_url_opener.open(req)
        result = conn.read()
        return result == "true"

    def setCodeCellEvaluator(self, name, evaluator):
        args = {'name': name, 'evaluator': evaluator, 'session': self.session_id}
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/setCodeCellEvaluator',
                                     urllib.parse.urlencode(args).encode('utf8'))
        conn = self._beaker_url_opener.open(req)
        result = conn.read()
        return result == "true"

    def setCodeCellTags(self, name, tags):
        args = {'name': name, 'tags': tags, 'session': self.session_id}
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/setCodeCellTags',
                                     urllib.parse.urlencode(args).encode('utf8'))
        conn = self._beaker_url_opener.open(req)
        result = conn.read()
        return result == "true"

    def runByTag(self, tag):
        arguments = dict(target_name='beakerx.tag.run')
        comm = Comm(**arguments)
        msg = {'runByTag': tag}
        state = {'state': msg}
        comm.send(data=state, buffers=[])

    def urlArg(self, argName):
        arguments = dict(target_name='beakerx.geturlarg')
        comm = Comm(**arguments)
        state = {
            'name': 'URL_ARG',
            'arg_name': argName
        }
        data = {
            'state': state,
            'url': self._url,
            'type': 'python'
        }

        comm.send(data=data, buffers=[])
        data = self._queue.get()
        params = json.loads(data)
        return params['argValue']

    def __setattr__(self, name, value):
        if 'session_id' == name:
            self.__dict__['session_id'] = value
            return
        if '_comm' == name:
            self.__dict__['_comm'] = value
            return
        if '_url' == name:
            self.__dict__['_url'] = value
            return
        if '_queue' == name:
            self.__dict__['_queue'] = value
            return
        if '_server' == name:
            self.__dict__['_server'] = value
            return
        return self.set(name, value)

    def __getattr__(self, name):
        if '_comm' == name:
            return self.__dict__['_comm']
        if '_url' == name:
            return self.__dict__['_url']
        if '_queue' == name:
            return self.__dict__['_queue']
        if '_server' == name:
            return self.__dict__['_server']
        return self.get(name)

    def __contains__(self, name):
        return self.isDefined(name)

    def __delattr__(self, name):
        return self.unset(name)
示例#5
0
class BeakerX:

    def __init__(self):
        self._comm = None
        self._queue = Queue()
        self._server = BeakerxZMQServer(self._queue)
        self._url = self._server.url
        if BeakerXTabledisplay is not None:
            BeakerXTabledisplay.pandas_display_table()

    @staticmethod
    def pandas_display_default():
        pandas.DataFrame._ipython_display_ = None

    @staticmethod
    def pandas_display_table():
        if BeakerXTabledisplay is not None:
            BeakerXTabledisplay.pandas_display_table()
        else:
            html = BeakerxHTML()
            html.value = 'You need beakerx_tabledisplay to use this'
            IPython.display.display_html(html)

    def set4(self, var, val, unset, sync):
        args = {'name': var, 'sync': sync}
        if not unset:
            val = transform(val)
            args['value'] = json.dumps(val, cls=DataFrameEncoder)
            state = {'state': args}
        if self._comm is None:
            self.init_autotranslation_comm()
        self._comm.send(data=state)

    def init_autotranslation_comm(self):
        self._comm = Comm(target_name='beakerx.autotranslation')
        self._comm.open()

    def get(self, var):
        result = autotranslation_get(var)
        if result == 'undefined':
            raise NameError('name \'' + var + '\' is not defined on the beakerx object')
        return transformBack(json.loads(result))

    def set_session(self, id):
        self.session_id = id

    def register_output(self):
        ip = IPython.InteractiveShell.instance()
        ip.display_formatter.formatters['application/json'] = MyJSONFormatter(parent=ip.display_formatter)

    def set(self, var, val):
        autotranslation_update(var, val)
        return self.set4(var, val, False, True)

    def unset(self, var):
        return self.set4(var, None, True, True)

    def isDefined(self, var):
        return autotranslation_get(var) != 'undefined'

    def createOutputContainer(self):
        return OutputContainer()

    def showProgressUpdate(self):
        return "WARNING: python3 language plugin does not support progress updates"

    def evaluate(self, filter):
        args = {'filter': filter, 'session': self.session_id}
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/evaluate',
                                     urllib.parse.urlencode(args).encode('utf8'))
        conn = self._beaker_url_opener.open(req)
        result = json.loads(conn.read().decode())
        return transformBack(result)

    def evaluateCode(self, evaluator, code):
        args = {'evaluator': evaluator, 'code': code, 'session': self.session_id}
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/evaluateCode',
                                     urllib.parse.urlencode(args).encode('utf8'))
        conn = self._beaker_url_opener.open(req)
        result = json.loads(conn.read().decode())
        return transformBack(result)

    def showStatus(self, msg):
        args = {'msg': msg, 'session': self.session_id}
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/showStatus',
                                     urllib.parse.urlencode(args).encode('utf8'))
        conn = self._beaker_url_opener.open(req)
        result = conn.read()
        return result == "true"

    def clearStatus(self, msg):
        args = {'msg': msg, 'session': self.session_id}
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/clearStatus',
                                     urllib.parse.urlencode(args).encode('utf8'))
        conn = self._beaker_url_opener.open(req)
        result = conn.read()
        return result == "true"

    def showTransientStatus(self, msg):
        args = {'msg': msg, 'session': self.session_id}
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/showTransientStatus',
                                     urllib.parse.urlencode(args).encode('utf8'))
        conn = self._beaker_url_opener.open(req)
        result = conn.read()
        return result == "true"

    def getEvaluators(self):
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/getEvaluators?' +
                                     urllib.parse.urlencode({
                                         'session': self.session_id}))
        conn = self._beaker_url_opener.open(req)
        result = json.loads(conn.read().decode())
        return transformBack(result)

    def getVersion(self):
        req = urllib.request.Request(
            self.core_url + '/rest/util/version?' + urllib.parse.urlencode({'session': self.session_id}))
        conn = self._beaker_url_opener.open(req)
        return transformBack(conn.read().decode())

    def getVersionNumber(self):
        req = urllib.request.Request(
            self.core_url + '/rest/util/getVersionInfo?' + urllib.parse.urlencode({'session': self.session_id}))
        conn = self._beaker_url_opener.open(req)
        result = json.loads(conn.read().decode())
        return transformBack(result['version'])

    def getCodeCells(self, filter):
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/getCodeCells?' +
                                     urllib.parse.urlencode({
                                         'filter': filter}))
        conn = self._beaker_url_opener.open(req)
        result = json.loads(conn.read().decode())
        return transformBack(result)

    def setCodeCellBody(self, name, body):
        args = {'name': name, 'body': body, 'session': self.session_id}
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/setCodeCellBody',
                                     urllib.parse.urlencode(args).encode('utf8'))
        conn = self._beaker_url_opener.open(req)
        result = conn.read()
        return result == "true"

    def setCodeCellEvaluator(self, name, evaluator):
        args = {'name': name, 'evaluator': evaluator, 'session': self.session_id}
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/setCodeCellEvaluator',
                                     urllib.parse.urlencode(args).encode('utf8'))
        conn = self._beaker_url_opener.open(req)
        result = conn.read()
        return result == "true"

    def setCodeCellTags(self, name, tags):
        args = {'name': name, 'tags': tags, 'session': self.session_id}
        req = urllib.request.Request(self.core_url + '/rest/notebookctrl/setCodeCellTags',
                                     urllib.parse.urlencode(args).encode('utf8'))
        conn = self._beaker_url_opener.open(req)
        result = conn.read()
        return result == "true"

    def runByTag(self, tag):
        arguments = dict(target_name='beakerx.tag.run')
        comm = Comm(**arguments)
        msg = {'runByTag': tag}
        state = {'state': msg}
        comm.send(data=state, buffers=[])

    def urlArg(self, argName):
        arguments = dict(target_name='beakerx.geturlarg')
        comm = Comm(**arguments)
        state = {
            'name': 'URL_ARG',
            'arg_name': argName
        }
        data = {
            'state': state,
            'url': self._url,
            'type': 'python'
        }

        comm.send(data=data, buffers=[])
        data = self._queue.get()
        params = json.loads(data)
        return params['argValue']

    def __setattr__(self, name, value):
        if 'session_id' == name:
            self.__dict__['session_id'] = value
            return
        if '_comm' == name:
            self.__dict__['_comm'] = value
            return
        if '_url' == name:
            self.__dict__['_url'] = value
            return
        if '_queue' == name:
            self.__dict__['_queue'] = value
            return
        if '_server' == name:
            self.__dict__['_server'] = value
            return
        return self.set(name, value)

    def __getattr__(self, name):
        if '_comm' == name:
            return self.__dict__['_comm']
        if '_url' == name:
            return self.__dict__['_url']
        if '_queue' == name:
            return self.__dict__['_queue']
        if '_server' == name:
            return self.__dict__['_server']
        return self.get(name)

    def __contains__(self, name):
        return self.isDefined(name)

    def __delattr__(self, name):
        return self.unset(name)
示例#6
0
class WWTLabApplication(BaseWWTWidget):
    """
    A handle the WWT JupyterLab application.

    While other parts of pywwt create "widgets", bound to variables running
    inside Python notebooks, this class represents a connection to the
    standalone "application", which exists in JupyterLab independently of any
    one specific notebook. The Python API is the same, it's just that the JSON
    messages we send are routed to the separate application rather than our own
    iframe.
    """

    _comm = None
    _controls = None

    def __init__(self):
        _maybe_perpetrate_mega_kernel_hack()

        self._comm = Comm(target_name='@wwtelescope/jupyterlab:research',
                          data={})
        self._comm.on_msg(self._on_comm_message_received)
        self._comm.open()

        super(WWTLabApplication, self).__init__()

    def _on_comm_message_received(self, msg):
        """
        Called when we receive a comms message.

        NOTE: because this code is run asynchronously in Jupyter's comms
        architecture, exceptions and printouts don't get reported to the user --
        they just disappear. I don't know if there's a "right" way to address
        that.
        """
        payload = msg['content']['data']

        # Special message from the hub indicating app liveness status
        if payload.get('type') == 'wwt_jupyter_viewer_status':
            self._on_app_status_change(alive=payload['alive'])
            # don't return -- maybe someone downstream can use this, and message
            # processing needs to handle all sorts of unexpected messages anyway

        self._on_app_message_received(payload)

    def _actually_send_msg(self, payload):
        self._comm.send(payload)

    def _serve_file(self, filename, extension=''):
        return serve_file(filename, extension=extension)

    def _create_image_layer(self, **kwargs):
        """Returns a specialized subclass of ImageLayer that has some extra hooks for
        creating UI control points.

        """
        return JupyterImageLayer(parent=self, **kwargs)

    @property
    def layer_controls(self):
        if self._controls is None:
            opacity_slider = widgets.FloatSlider(value=self.foreground_opacity,
                                                 min=0,
                                                 max=1,
                                                 readout=False)
            foreground_menu = widgets.Dropdown(options=self.available_layers,
                                               value=self.foreground)
            background_menu = widgets.Dropdown(options=self.available_layers,
                                               value=self.background)
            link((opacity_slider, 'value'), (self, 'foreground_opacity'))
            link((foreground_menu, 'value'), (self, 'foreground'))
            link((background_menu, 'value'), (self, 'background'))
            self._controls = widgets.HBox(
                [background_menu, opacity_slider, foreground_menu])
        return self._controls
示例#7
0
class Nep:
    # def __init__(self,comm_name=None):
    #     if comm_name is None:
    #         comm_name=str(uuid.uuid4())
    def __init__(self, comm=None, kernel_id=None):
        # self.comm = Comm(target_name=comm_name)
        # self.comm = Comm(target_name="neos_comm")
        print(kernel_id)
        self.kernel_id = kernel_id
        if comm is None:
            self.comm = Comm(target_name="neos_comm")
        else:
            self.comm = comm
        self.comm.open()
        self.vars = Variables(self.comm, self)
        self.comm.on_msg(self._on_msg)
        self.vars_to_update = []
        self.var_types = {}
        self.neos_updates_locked = False
        self.var_temp_vals = {}

    def start(self,
              base='http://localhost:8888',
              notebook_path='/Untitled.ipynb',
              auth_token='',
              ws_port=8766):
        if self.kernel_id is None:
            server_process = Process(target=run_server,
                                     args=(self.comm.comm_id, base,
                                           notebook_path, auth_token, ws_port),
                                     daemon=True)
        else:
            server_process = Process(target=run_server_from_id,
                                     args=(self.comm.comm_id, self.kernel_id,
                                           auth_token, ws_port),
                                     daemon=True)
        server_process.start()
        #guido forgive me coz i know this is ugly
        static_server_process = Process(target=subprocess.call,
                                        args=('python -m http.server 8000', ),
                                        kwargs={"shell": True})
        static_server_process.start()

    #TODO: nep.stop() !!

    def _on_msg(self, msg):
        #handler for message recived for this Nep
        #we update the value of the variable
        msg = msg["content"]["data"]
        i = msg.index("/")
        msg_format_correct = (i != -1)
        if msg_format_correct:
            varname = msg[:i]
            if varname in self.vars_to_update:
                val_str = msg[i + 1:]
                if self.var_types[varname] == "float":
                    varvalue = float(val_str)
                elif self.var_types[varname] == "int":
                    varvalue = int(val_str)
                elif self.var_types[varname] == "float_vec":
                    val_str = val_str[1:-1]
                    varvalue = tuple([float(x) for x in val_str.split(";")])
                elif self.var_types[varname] == "int_vec":
                    val_str = val_str[1:-1]
                    varvalue = tuple([int(x) for x in val_str.split(";")])
                elif self.var_types[varname] == "list":
                    varvalue = val_str.split("|")[:-1]
                else:
                    varvalue = val_str
                if not self.neos_updates_locked:
                    setattr(Variables, "_" + varname, varvalue)
                else:
                    self.var_temp_vals[varname] = varvalue
            else:
                print("Warning: Neos is trying to update variable " + varname +
                      " that is not Nep's vars_to_update")
        else:
            print(
                "Warning: Neos message type not supported (it doesn't have the format varname/varvalue)"
            )

    def _send_var(self, var_name, var_value):
        var_type = type(var_value)
        value_str = ""
        if var_type is str:
            value_str = var_value
        elif var_type is tuple:
            value_str = "[" + ";" + join([str(x) for x in var_value]) + "]"
        elif var_type is list:
            value_str = "|" + join([str(x) for x in var_value]) + "|"
        else:
            value_str = str(var_value)
        self.comm.send("updateVar/" + var_name + "/" + value_str)

    def send(self, var_name, custom_name=None, value=None):
        var_value = value
        #IDEA: Maybe put this functionality in another method. send_custom or something!
        if value is None:
            frame = inspect.currentframe()
            locals = frame.f_back.f_locals  # local variables from calling scope
            var_value = locals[var_name]

        if custom_name is not None:
            var_name = custom_name
        self._send_var(var_name, var_value)

    def bind(self,
             varname,
             callback=None,
             type="float",
             update_neos=True,
             update_python=True):
        prop = property(fset=Variables._generate_set(varname, update_neos,
                                                     callback),
                        fget=lambda self: Variables.__dict__["_" + varname],
                        fdel=Variables._generate_del(varname, update_neos))
        setattr(Variables, "_" + varname, None)
        setattr(Variables, varname, prop)
        self.comm.send("addVar/" + varname)
        if update_python:
            if varname not in self.vars_to_update:
                self.vars_to_update.append(varname)
                self.var_types[varname] = type

    def plot(self, plt):
        plt.plot()
        figname = "img/" + uuid.uuid1().hex + ".png"
        plt.savefig(figname)
        self.comm.send("media/" + "http://localhost:8000/" + figname)

    def listen(self, varname):
        frame = inspect.currentframe()
        locals = frame.f_back.f_locals  # local variables from calling scope
        #TODO: this one only upates the local variable when neos changes the variable

    def lock(self):
        #freeze updating of variables from Neos, and instead update to a temp storage of variables
        self.neos_updates_locked = True

    def unlock(self):
        #unfreeze the variables from Neos, and update them according to the stored updates
        self.neos_updates_locked = False
        for varname in self.var_temp_vals:
            setattr(Variables, "_" + varname, self.var_temp_vals[varname])
        self.var_temp_vals = {}

    def reactive_loop(self, function, iterable, *args, **kwargs):
        #TODO: iterate function with iterable, unlocking and locking the self.vars before every iteration.
        # run iteration in another thread to allow for neos to update the variables between each iteration
        def loop():
            for it in iterable:
                self.lock()
                function(it, *args, **kwargs)
                self.unlock()

        t = threading.Thread(target=loop)
        t.start()
        pass