Commits

Matthew Frazier committed 397f321

brought the project layout up to standard

Comments (0)

Files changed (4)

+Copyright (c) 2011 Matthew Frazier
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
 ============
 This is a Python library that interacts with fishd, the `fish`_ universal
 variable daemon. You can send and receive commands to and from the daemon, and
-use the ``UniversalVariables`` class to work with universal variables like you
+use the `UniversalVariables` class to work with universal variables like you
 would in an actual fish shell.
 
 However, currently python-fishd does not unescape non-ASCII characters received
 
 python-fishd's homepage is at http://bitbucket.org/leafstorm/python-fishd/.
 
-.. _fish: http://www.fishshell.org/
+.. _fish: http://www.fishshell.org/
+# -*- coding: utf-8 -*-
+"""
+fishd
+=====
+A library that interacts with fishd, the fish universal variable daemon.
+
+:copyright: (c) 2011 Matthew Frazier
+:license:   MIT/X11, see LICENSE for details
+"""
+
+import os
+import socket
+try:
+    from cStringIO import StringIO
+except ImportError:
+    from StringIO import StringIO
+
+DEL = r'\x1e'
+NULL = r'\x1d'
+ARRAY = (list, tuple)
+
+
+def serialize(value):
+    """
+    This will serialize variables. Zero-element lists and tuples will be
+    converted to a zero-element array. Lists and tuples will be converted into
+    arrays, and anything else will be converted to a string.
+    """
+    if isinstance(value, ARRAY):
+        if len(value) == 0:
+            return NULL
+        else:
+            return DEL.join(seq)
+    else:
+        return str(value)
+
+
+def deserialize(value, force_array=False):
+    """
+    This will automatically deserialize variables. Zero-element arrays will
+    be converted to empty tuples, multi-element arrays will be converted to
+    tuples, and strings will be left as is. If ``force_array`` is True,
+    strings will be converted to a one-element tuple.
+    """
+    if value == NULL:
+        return ()
+    elif (DEL in value) or force_array:
+        return tuple(value.split(DEL))
+    else:
+        return value
+
+
+def parse_command(command, force_array=False):
+    """
+    This converts a command without a newline (like you would get from
+    ``Fishd.recv_commands``) into a ``FishdCmd`` instance. If ``force_array``
+    is True, strings get converted to a one-element tuple.
+    """
+    cmdlist = command.split(' ', 1)
+    cmdname = cmdlist[0].lower()
+    if cmdname == 'set':
+        name, value = cmdlist[1].split(':', 1)
+        return SetCmd(name, deserialize(value, force_array))
+    elif cmdname == 'set_export':
+        name, value = cmdlist[1].split(':', 1)
+        return SetExportCmd(name, deserialize(value, force_array))
+    elif cmdname == 'erase':
+        return EraseCmd(cmdlist[1])
+    elif cmdname == 'barrier':
+        return BarrierCmd()
+    elif cmdname == 'barrier_reply':
+        return BarrierReplyCmd()
+
+
+class FishdCmd(object):
+    """
+    This class and its subclasses can be used to construct commands that can
+    be sent using ``fishd.send_command()``. This one doesn't do anything.
+    """
+    def __str__(self):
+        return self.command
+    
+    def __repr__(self):
+        return '%s()' % self.__class__.__name__
+
+
+class SetCmd(FishdCmd):
+    """
+    This represents the fishd ``set`` command. Create this with the variable's
+    name and value. For details on what it does, see ``Fishd.set``.
+    """
+    command = 'set'
+    
+    def __init__(self, name, value):
+        self.name = name
+        self.value = value
+    
+    def __str__(self):
+        return '%s %s:%s' % (self.command, self.name,
+                             serialize(self.value))
+    
+    def __repr__(self):
+        return '%s(%r, %r)' % (self.__class__.__name__, self.name, self.value)
+
+
+class SetExportCmd(SetCmd):
+    """
+    This represents the fishd ``set_export`` command. Create this with the
+    variable's name and value. For details on what it does, see
+    ``Fishd.set_export``.
+    """
+    command = 'set_export'
+
+
+class EraseCmd(FishdCmd):
+    """
+    This represents the fishd ``erase`` command. Create this with the name of
+    the variable to erase. For details on what it does, see ``Fishd.erase``.
+    """
+    command = 'erase'
+    
+    def __init__(self, name):
+        self.name = name
+    
+    def __str__(self):
+        return '%s %s' % (self.command, self.name)
+    
+    def __repr__(self):
+        return '%s(%r)' % (self.__class__.__name__, self.name)
+
+
+class BarrierCmd(FishdCmd):
+    """
+    This represents the fishd ``barrier`` command. It takes no arguments. For
+    details on what it does, see ``Fishd.barrier``.
+    """
+    command = 'barrier'
+
+
+class BarrierReplyCmd(FishdCmd):
+    """
+    This represents the fishd ``barrier_reply`` command. It takes no
+    arguments. For details on what it does, see ``Fishd.barrier_reply``.
+    """
+    command = 'barrier_reply'
+
+
+class Fishd(object):
+    @classmethod
+    def personal(cls):
+        """
+        This returns a Fishd for the current user. It gets the username from
+        the USER environment variable.
+        """
+        return cls('/tmp/fishd.socket.%s' % os.environ['USER'])
+    
+    @classmethod
+    def for_user(cls, user):
+        """
+        This returns a Fishd for the given username.
+        """
+        return cls('/tmp/fishd.socket.%s' % user)
+    
+    def __init__(self, path):
+        """
+        Creates a fishd connection for the given path. If you want to
+        automatically connect to a user's fishd, use the ``personal`` or
+        ``for_user`` class methods.
+        """
+        self.sockpath = path
+        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
+        self.sock.connect(path)
+    
+    def send_command(self, command):
+        """
+        This will send a command to fishd. A newline will be automatically
+        appended.
+        """
+        self.sock.send('%s\n' % command)
+    
+    def recv_commands(self, block=True):
+        """
+        This will read from the fishd socket and check for new commands that
+        fishd has sent. If ``block`` is True, the system will wait for
+        commands before returning. If ``block`` is False, the system will
+        return ``[]`` (the empty list) if there are no commands. The commands
+        will be returned as a list of strings.
+        """
+        sio = StringIO()
+        if not block:
+            self.sock.setblocking(0)
+        try:
+            while True:
+                data = self.sock.recv(4096)
+                sio.write(data)
+                if len(data) < 4096:
+                    break
+        except socket.error, e:
+            if not block:
+                self.sock.setblocking(1)
+                return []
+            else:
+                raise
+        if not block:
+            self.sock.setblocking(1)
+        lines = sio.getvalue().splitlines()
+        return [line for line in lines if line and (not line.startswith('#'))]
+    
+    def get_commands(self, force_array=False, block=True):
+        """
+        This will fetch the commands (``block`` works as it does in
+        ``recv_commands``) and parse them into ``FishdCmd`` instances. If
+        ``force_array`` is True, all variables will be deserialized to an
+        array, even if they don't have the array delimiter in them.
+        """
+        commands = self.recv_commands(block)
+        return [parse_command(command, force_array) for command in commands]
+    
+    def set(self, name, value):
+        """
+        This sends a ``set`` command to fishd. This will update the value of
+        the variable to ``value``, and if it is currently exported, it will
+        be unexported.
+        """
+        self.send_command('set %s:%s' % (name, serialize(value)))
+    
+    def set_export(self, name, value):
+        """
+        This sends a ``set_export`` command to fishd. This will update the
+        value of the variable to ``value``, and if it is currently not
+        exported, it will be exported.
+        """
+        self.send_command('set_export %s:%s' % (name, serialize(value)))
+    
+    def erase(self, name):
+        """
+        This sends an ``erase`` command to fishd. This will unset the
+        variable.
+        """
+        self.send_command('erase %s' % name)
+    
+    def barrier(self):
+        """
+        This sends a ``barrier`` command to fishd. This will make fishd send
+        a ``barrier_reply`` command back.
+        """
+        self.send_command('barrier')
+    
+    def barrier_reply(self):
+        """
+        This sends a ``barrier_reply`` command to fishd. This will alert fishd
+        that its ``barrier`` command was received. Don't send this unless
+        fishd sent you a ``barrier``.
+        """
+        self.send_command('barrier_reply')
+    
+    def close(self):
+        """
+        This will close the socket.
+        """
+        self.sock.close()
+
+
+class FishdHandler(object):
+    """
+    A class you can use to 'subscribe' to commands generated by fishd. When
+    the ``update()`` method is called, the system will fetch the commands and
+    pass every command off to the method ``handle_command`` for the lowercase
+    version of the command sent. By default, only an implementation for the
+    ``barrier`` command exists, which simply sends a ``barrier_reply``.
+    """
+    force_array = False
+    _print_cmds = False
+    
+    @classmethod
+    def connect_personal(cls, *args, **kwargs):
+        """
+        This will pass the current user's fishd, as well as any other
+        arguments, to the class constructor.
+        """
+        return cls(fishd.Fishd.personal(), *args, **kwargs)
+    
+    def __init__(self, fishd):
+        """
+        The constructor takes a Fishd instance. (To connect to the user's
+        personal fishd, you can use the ``connect_personal`` class method.)
+        It will assign it to ``self.fishd``, which you should too.
+        """
+        self.fishd = fishd
+    
+    def update(self, block=False):
+        """
+        This method will get the commands from the fishd and call the handler
+        methods for each command. By default, it is nonblocking, but if you
+        pass ``block=True`` it will block. If your program has its own main
+        loop like the GTK loop, you should call this when you can to ensure
+        that the commands are received as soon as possible.
+        """
+        cmds = self.fishd.get_commands(force_array=self.force_array,
+                                       block=block)
+        for command in cmds:
+            if self._print_cmds: print command
+            try:
+                getattr(self, 'handle_%s' % command.command)(command)
+            except AttributeError:
+                self.handle_other_command(command)
+    
+    def main_loop(self):
+        """
+        This just runs ``update`` forever. The only way to break is out is an
+        exception, including ``KeyboardInterrupt``.
+        """
+        while True:
+            self.update()
+    
+    def handle_barrier(self):
+        """
+        This is the default barrier implementation. It will simply send back
+        a barrier_reply to the fishd.
+        """
+        self.fishd.barrier_reply()
+    
+    def handle_other_command(self, command):
+        """
+        This is called when a command is received that there is not a handler
+        method for. By default, it does nothing.
+        """
+        pass
+
+
+class UniversalVars(FishdHandler):
+    """
+    This class tracks universal variables like fish would. It has a dictionary
+    and an export set (``vars`` and ``exports``) that indicates which
+    variables are exported.
+    
+    You should pass in a "clean" fishd instance that hasn't sent or received
+    any commands yet. Also, if it modifies or erases any variables, it won't
+    actually change the internal dictionary - you have to run update() again
+    for the changes to be reflected in the variables. Because of this, messing
+    around with the ``vars`` or ``exports`` attributes will BREAK EVERYTHING.
+    
+    It will raise KeyError like usual if a reference is made to a variable
+    that does not exist.
+    """
+    def __init__(self, fishd):
+        self.fishd = fishd
+        self.vars = {}
+        self.exports = set()
+    
+    def get(self, var):
+        return self.vars[var]
+    
+    def set(self, var, value):
+        """
+        This will set the value of the variable universally, maintaining its
+        exported status. The changes will not be reflected until ``update()``
+        is run.
+        """
+        if var in self.exports:
+            self.fishd.set_export(var, value)
+        else:
+            self.fishd.set(var, value)
+    
+    def erase(self, var):
+        """
+        This will erase a variable universally. The changes will not be
+        reflected until ``update()`` is run.
+        """
+        self.fishd.erase(var)
+    
+    def export(self, var):
+        """
+        This will mark a variable as exported universally. The changes will
+        not be reflected until ``update()`` is run.
+        """
+        self.fishd.set_export(var, self.vars[var])
+    
+    def unexport(self, var):
+        """
+        This will mark a variable as not exported universally. The changes
+        will not be reflected until ``update()`` is run.
+        """
+        self.fishd.set(var, self.vars[var])
+    
+    def handle_set(self, cmd):
+        self.vars[cmd.name] = cmd.value
+        if cmd.name in self.exports:
+            self.exports.remove(cmd.name)
+    
+    def handle_set_export(self, cmd):
+        self.vars[cmd.name] = cmd.value
+        if cmd.name not in self.exports:
+            self.exports.add(cmd.name)
+    
+    def handle_erase(self, cmd):
+        del self.vars[cmd.name]
+        if cmd.name in self.exports:
+            self.exports.remove(cmd.name)
+    
+    @property
+    def exported(self):
+        return dict(((k, v) for (k, v) in self.vars.items()
+                     if k in self.exports))
+    
+    def __getitem__(self, key):
+        return self.vars[key]
+    
+    def __setitem__(self, key, value):
+        self.set(key, value)
+    
+    def __delitem__(self, key):
+        self.erase(key)

lib/fishd.py

-# fishd.py
-# A Python library for communicating with the fish universal variable daemon
-
-import os
-import socket
-try:
-    from cStringIO import StringIO
-except ImportError:
-    from StringIO import StringIO
-
-DEL = r'\x1e'
-NULL = r'\x1d'
-ARRAY = (list, tuple)
-
-
-def serialize(value):
-    """
-    This will serialize variables. Zero-element lists and tuples will be
-    converted to a zero-element array. Lists and tuples will be converted into
-    arrays, and anything else will be converted to a string.
-    """
-    if isinstance(value, ARRAY):
-        if len(value) == 0:
-            return NULL
-        else:
-            return DEL.join(seq)
-    else:
-        return str(value)
-
-
-def deserialize(value, force_array=False):
-    """
-    This will automatically deserialize variables. Zero-element arrays will
-    be converted to empty tuples, multi-element arrays will be converted to
-    tuples, and strings will be left as is. If ``force_array`` is True,
-    strings will be converted to a one-element tuple.
-    """
-    if value == NULL:
-        return ()
-    elif (DEL in value) or force_array:
-        return tuple(value.split(DEL))
-    else:
-        return value
-
-
-def parse_command(command, force_array=False):
-    """
-    This converts a command without a newline (like you would get from
-    ``Fishd.recv_commands``) into a ``FishdCmd`` instance. If ``force_array``
-    is True, strings get converted to a one-element tuple.
-    """
-    cmdlist = command.split(' ', 1)
-    cmdname = cmdlist[0].lower()
-    if cmdname == 'set':
-        name, value = cmdlist[1].split(':', 1)
-        return SetCmd(name, deserialize(value, force_array))
-    elif cmdname == 'set_export':
-        name, value = cmdlist[1].split(':', 1)
-        return SetExportCmd(name, deserialize(value, force_array))
-    elif cmdname == 'erase':
-        return EraseCmd(cmdlist[1])
-    elif cmdname == 'barrier':
-        return BarrierCmd()
-    elif cmdname == 'barrier_reply':
-        return BarrierReplyCmd()
-
-
-class FishdCmd(object):
-    """
-    This class and its subclasses can be used to construct commands that can
-    be sent using ``fishd.send_command()``. This one doesn't do anything.
-    """
-    def __str__(self):
-        return self.command
-    
-    def __repr__(self):
-        return '%s()' % self.__class__.__name__
-
-
-class SetCmd(FishdCmd):
-    """
-    This represents the fishd ``set`` command. Create this with the variable's
-    name and value. For details on what it does, see ``Fishd.set``.
-    """
-    command = 'set'
-    
-    def __init__(self, name, value):
-        self.name = name
-        self.value = value
-    
-    def __str__(self):
-        return '%s %s:%s' % (self.command, self.name,
-                             serialize(self.value))
-    
-    def __repr__(self):
-        return '%s(%r, %r)' % (self.__class__.__name__, self.name, self.value)
-
-
-class SetExportCmd(SetCmd):
-    """
-    This represents the fishd ``set_export`` command. Create this with the
-    variable's name and value. For details on what it does, see
-    ``Fishd.set_export``.
-    """
-    command = 'set_export'
-
-
-class EraseCmd(FishdCmd):
-    """
-    This represents the fishd ``erase`` command. Create this with the name of
-    the variable to erase. For details on what it does, see ``Fishd.erase``.
-    """
-    command = 'erase'
-    
-    def __init__(self, name):
-        self.name = name
-    
-    def __str__(self):
-        return '%s %s' % (self.command, self.name)
-    
-    def __repr__(self):
-        return '%s(%r)' % (self.__class__.__name__, self.name)
-
-
-class BarrierCmd(FishdCmd):
-    """
-    This represents the fishd ``barrier`` command. It takes no arguments. For
-    details on what it does, see ``Fishd.barrier``.
-    """
-    command = 'barrier'
-
-
-class BarrierReplyCmd(FishdCmd):
-    """
-    This represents the fishd ``barrier_reply`` command. It takes no
-    arguments. For details on what it does, see ``Fishd.barrier_reply``.
-    """
-    command = 'barrier_reply'
-
-
-class Fishd(object):
-    @classmethod
-    def personal(cls):
-        """
-        This returns a Fishd for the current user. It gets the username from
-        the USER environment variable.
-        """
-        return cls('/tmp/fishd.socket.%s' % os.environ['USER'])
-    
-    @classmethod
-    def for_user(cls, user):
-        """
-        This returns a Fishd for the given username.
-        """
-        return cls('/tmp/fishd.socket.%s' % user)
-    
-    def __init__(self, path):
-        """
-        Creates a fishd connection for the given path. If you want to
-        automatically connect to a user's fishd, use the ``personal`` or
-        ``for_user`` class methods.
-        """
-        self.sockpath = path
-        self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-        self.sock.connect(path)
-    
-    def send_command(self, command):
-        """
-        This will send a command to fishd. A newline will be automatically
-        appended.
-        """
-        self.sock.send('%s\n' % command)
-    
-    def recv_commands(self, block=True):
-        """
-        This will read from the fishd socket and check for new commands that
-        fishd has sent. If ``block`` is True, the system will wait for
-        commands before returning. If ``block`` is False, the system will
-        return ``[]`` (the empty list) if there are no commands. The commands
-        will be returned as a list of strings.
-        """
-        sio = StringIO()
-        if not block:
-            self.sock.setblocking(0)
-        try:
-            while True:
-                data = self.sock.recv(4096)
-                sio.write(data)
-                if len(data) < 4096:
-                    break
-        except socket.error, e:
-            if not block:
-                self.sock.setblocking(1)
-                return []
-            else:
-                raise
-        if not block:
-            self.sock.setblocking(1)
-        lines = sio.getvalue().splitlines()
-        return [line for line in lines if line and (not line.startswith('#'))]
-    
-    def get_commands(self, force_array=False, block=True):
-        """
-        This will fetch the commands (``block`` works as it does in
-        ``recv_commands``) and parse them into ``FishdCmd`` instances. If
-        ``force_array`` is True, all variables will be deserialized to an
-        array, even if they don't have the array delimiter in them.
-        """
-        commands = self.recv_commands(block)
-        return [parse_command(command, force_array) for command in commands]
-    
-    def set(self, name, value):
-        """
-        This sends a ``set`` command to fishd. This will update the value of
-        the variable to ``value``, and if it is currently exported, it will
-        be unexported.
-        """
-        self.send_command('set %s:%s' % (name, serialize(value)))
-    
-    def set_export(self, name, value):
-        """
-        This sends a ``set_export`` command to fishd. This will update the
-        value of the variable to ``value``, and if it is currently not
-        exported, it will be exported.
-        """
-        self.send_command('set_export %s:%s' % (name, serialize(value)))
-    
-    def erase(self, name):
-        """
-        This sends an ``erase`` command to fishd. This will unset the
-        variable.
-        """
-        self.send_command('erase %s' % name)
-    
-    def barrier(self):
-        """
-        This sends a ``barrier`` command to fishd. This will make fishd send
-        a ``barrier_reply`` command back.
-        """
-        self.send_command('barrier')
-    
-    def barrier_reply(self):
-        """
-        This sends a ``barrier_reply`` command to fishd. This will alert fishd
-        that its ``barrier`` command was received. Don't send this unless
-        fishd sent you a ``barrier``.
-        """
-        self.send_command('barrier_reply')
-    
-    def close(self):
-        """
-        This will close the socket.
-        """
-        self.sock.close()
-
-
-class FishdHandler(object):
-    """
-    A class you can use to 'subscribe' to commands generated by fishd. When
-    the ``update()`` method is called, the system will fetch the commands and
-    pass every command off to the method ``handle_command`` for the lowercase
-    version of the command sent. By default, only an implementation for the
-    ``barrier`` command exists, which simply sends a ``barrier_reply``.
-    """
-    force_array = False
-    _print_cmds = False
-    
-    @classmethod
-    def connect_personal(cls, *args, **kwargs):
-        """
-        This will pass the current user's fishd, as well as any other
-        arguments, to the class constructor.
-        """
-        return cls(fishd.Fishd.personal(), *args, **kwargs)
-    
-    def __init__(self, fishd):
-        """
-        The constructor takes a Fishd instance. (To connect to the user's
-        personal fishd, you can use the ``connect_personal`` class method.)
-        It will assign it to ``self.fishd``, which you should too.
-        """
-        self.fishd = fishd
-    
-    def update(self, block=False):
-        """
-        This method will get the commands from the fishd and call the handler
-        methods for each command. By default, it is nonblocking, but if you
-        pass ``block=True`` it will block. If your program has its own main
-        loop like the GTK loop, you should call this when you can to ensure
-        that the commands are received as soon as possible.
-        """
-        cmds = self.fishd.get_commands(force_array=self.force_array,
-                                       block=block)
-        for command in cmds:
-            if self._print_cmds: print command
-            try:
-                getattr(self, 'handle_%s' % command.command)(command)
-            except AttributeError:
-                self.handle_other_command(command)
-    
-    def main_loop(self):
-        """
-        This just runs ``update`` forever. The only way to break is out is an
-        exception, including ``KeyboardInterrupt``.
-        """
-        while True:
-            self.update()
-    
-    def handle_barrier(self):
-        """
-        This is the default barrier implementation. It will simply send back
-        a barrier_reply to the fishd.
-        """
-        self.fishd.barrier_reply()
-    
-    def handle_other_command(self, command):
-        """
-        This is called when a command is received that there is not a handler
-        method for. By default, it does nothing.
-        """
-        pass
-
-
-class UniversalVars(FishdHandler):
-    """
-    This class tracks universal variables like fish would. It has a dictionary
-    and an export set (``vars`` and ``exports``) that indicates which
-    variables are exported.
-    
-    You should pass in a "clean" fishd instance that hasn't sent or received
-    any commands yet. Also, if it modifies or erases any variables, it won't
-    actually change the internal dictionary - you have to run update() again
-    for the changes to be reflected in the variables. Because of this, messing
-    around with the ``vars`` or ``exports`` attributes will BREAK EVERYTHING.
-    
-    It will raise KeyError like usual if a reference is made to a variable
-    that does not exist.
-    """
-    def __init__(self, fishd):
-        self.fishd = fishd
-        self.vars = {}
-        self.exports = set()
-    
-    def get(self, var):
-        return self.vars[var]
-    
-    def set(self, var, value):
-        """
-        This will set the value of the variable universally, maintaining its
-        exported status. The changes will not be reflected until ``update()``
-        is run.
-        """
-        if var in self.exports:
-            self.fishd.set_export(var, value)
-        else:
-            self.fishd.set(var, value)
-    
-    def erase(self, var):
-        """
-        This will erase a variable universally. The changes will not be
-        reflected until ``update()`` is run.
-        """
-        self.fishd.erase(var)
-    
-    def export(self, var):
-        """
-        This will mark a variable as exported universally. The changes will
-        not be reflected until ``update()`` is run.
-        """
-        self.fishd.set_export(var, self.vars[var])
-    
-    def unexport(self, var):
-        """
-        This will mark a variable as not exported universally. The changes
-        will not be reflected until ``update()`` is run.
-        """
-        self.fishd.set(var, self.vars[var])
-    
-    def handle_set(self, cmd):
-        self.vars[cmd.name] = cmd.value
-        if cmd.name in self.exports:
-            self.exports.remove(cmd.name)
-    
-    def handle_set_export(self, cmd):
-        self.vars[cmd.name] = cmd.value
-        if cmd.name not in self.exports:
-            self.exports.add(cmd.name)
-    
-    def handle_erase(self, cmd):
-        del self.vars[cmd.name]
-        if cmd.name in self.exports:
-            self.exports.remove(cmd.name)
-    
-    @property
-    def exported(self):
-        return dict(((k, v) for (k, v) in self.vars.items()
-                     if k in self.exports))
-    
-    def __getitem__(self, key):
-        return self.vars[key]
-    
-    def __setitem__(self, key, value):
-        self.set(key, value)
-    
-    def __delitem__(self, key):
-        self.erase(key)