Create a gist now

Instantly share code, notes, and snippets.

An attempt at modifying the default volumes plugin for kupfer to allow it to be more modular (as in only using gio for the unmount checks, which the unmount command actually handles the unmount).
__kupfer_name__ = _("Volumes and Disks (Custom Unmount)")
__kupfer_sources__ = ("VolumesSource", )
__description__ = _("Mounted volumes and disks")
__version__ = ""
__author__ = "Ulrik Sverdrup <>, Sapphira Armageddos <>"
import gio
from kupfer.objects import Action, Source, FileLeaf
from kupfer.obj.fileactions import Open, OpenTerminal
from kupfer import utils
from kupfer import plugin_support
from kupfer import pretty
from kupfer.objects import OperationError
__kupfer_settings__ = plugin_support.PluginSettings(
"key": "command",
"label": _("Command"),
"type": str,
"value": "unmount",
"tooltip": ("The command used to unmount or eject a device.")
class Volume (FileLeaf):
The Volume class actually represents one instance
of GIO's GMount (as long as it is mounted)
# NOTE: marking as non-serializable
serializable = None
def __init__(self, volume):
self.volume = volume
fil = self.volume.get_root()
path = fil.get_path()
super(Volume, self).__init__(obj=path, name=volume.get_name())
def get_actions(self):
yield Open()
yield OpenTerminal()
if self.volume.can_eject():
yield Eject()
elif self.volume.can_unmount():
yield Unmount()
def is_valid(self):
vm = gio.volume_monitor_get()
return any(self.volume == v for v in vm.get_mounts())
def get_description(self):
return _("Volume mounted at %s") % \
def get_gicon(self):
return self.volume.get_icon()
def get_icon_name(self):
return "drive-removable-media"
class Unmount (Action):
def __init__(self, name=None):
super(Unmount, self).__init__(name or _("Unmount"))
def wants_context(self):
return True
def unmount_callback(self, acommand, stdout, stderr, ctx):
"""Show async error if @acommand returns error output & error status.
Else post async result if @post_result.
pretty.print_debug(__name__, "Exited:", acommand)
if acommand.exit_status != 0:
if stderr:
errstr = kupferstring.fromlocale(stderr)[:max_error_msg]
errstr = kupferstring.fromlocale("An unknown error has occurred.")
def activate(self, leaf, ctx):
if not leaf.is_valid():
vol = leaf.volume
if vol.can_eject() or vol.can_unmount():
command = __kupfer_settings__["command"]
argv = utils.argv_for_commandline(command)
def finish_callback(acommand, stdout, stderr):
self.unmount_callback(acommand, stdout, stderr, ctx)
utils.AsyncCommand(argv, finish_callback, 15)
def get_description(self):
return _("Unmount this volume")
def get_icon_name(self):
return "media-eject"
class Eject (Unmount):
def __init__(self):
super(Eject, self).__init__(_("Eject"))
def get_description(self):
return _("Unmount and eject this media")
class VolumesSource (Source):
def __init__(self, name=_("Volumes and Disks")):
super(VolumesSource, self).__init__(name)
def is_dynamic(self):
return True
def get_items(self):
vm = gio.volume_monitor_get()
# get_mounts gets all mounted removable media
return (Volume(v) for v in vm.get_mounts())
def get_description(self):
return _("Mounted volumes and disks")
def get_icon_name(self):
return "drive-removable-media"
def provides(self):
yield Volume
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment