[kupfer: 11/41] vim: Use a D-Bus service in separate process



commit 23ae89d37f7c9ed5b2f69c8d8f248f46432805e0
Author: Ulrik Sverdrup <ulrik sverdrup gmail com>
Date:   Tue Apr 26 18:44:00 2011 +0200

    vim: Use a D-Bus service in separate process
    
    Move most of __init__.py into vim/plugin.py and only load it at
    initialize_plugin time.
    
    The kupfer.plugin.vim.service module is a D-Bus service intended to be
    run inside kupfer --exec-helper

 kupfer/plugin/vim/__init__.py |  270 +-----------------------------------
 kupfer/plugin/vim/plugin.py   |  310 +++++++++++++++++++++++++++++++++++++++++
 kupfer/plugin/vim/service.py  |  121 ++++++++++++++++
 3 files changed, 436 insertions(+), 265 deletions(-)
---
diff --git a/kupfer/plugin/vim/__init__.py b/kupfer/plugin/vim/__init__.py
index 53cc978..68dcc3f 100644
--- a/kupfer/plugin/vim/__init__.py
+++ b/kupfer/plugin/vim/__init__.py
@@ -5,269 +5,9 @@ __description__ = _("Recently used documents in Vim")
 __version__ = "2011-04"
 __author__ = "Plugin: Ulrik Sverdrup, VimCom: Ali Afshar"
 
-import os
 
-import gio
-import glib
-
-from kupfer.objects import Source, FileLeaf, Leaf, Action
-from kupfer.objects import OperationError
-from kupfer.objects import AppLeaf, TextLeaf, TextSource
-from kupfer.obj.objects import Launch
-from kupfer.obj.apps import AppLeafContentMixin
-from kupfer import datatools
-from kupfer import utils
-from kupfer import kupferstring
-
-from kupfer.plugin.vim import vimcom
-
-VIM = 'gvim'
-
-
-def get_vim_files(filepath):
-	"""
-	Read ~/.viminfo from @filepath
-
-	Look for a line like this:
-	*encoding=<encoding>
-
-	Return an iterator of unicode string file paths
-	"""
-	encoding = "UTF-8"
-	recents = []
-	with open(filepath, "r") as f:
-		for line in f:
-			if line.startswith("*encoding="):
-				_, enc = line.split("=")
-				encoding = enc.strip()
-			us_line = line.decode(encoding, "replace")
-			## Now find the jumplist
-			if us_line.startswith("-'  "):
-				parts = us_line.split(None, 3)
-				recentfile = os.path.expanduser(parts[-1].strip())
-				if recentfile:
-					recents.append(recentfile)
-	return datatools.UniqueIterator(recents)
-
-class RecentsSource (AppLeafContentMixin, Source):
-	appleaf_content_id = ("vim", "gvim")
-
-	vim_viminfo_file = "~/.viminfo"
-	def __init__(self, name=None):
-		name = name or _("Vim Recent Documents")
-		super(RecentsSource, self).__init__(name)
-
-	def initialize(self):
-		"""Set up change monitor"""
-		viminfofile = os.path.expanduser(self.vim_viminfo_file)
-		gfile = gio.File(viminfofile)
-		self.monitor = gfile.monitor_file(gio.FILE_MONITOR_NONE, None)
-		if self.monitor:
-			self.monitor.connect("changed", self._changed)
-
-	def finalize(self):
-		if self.monitor:
-			self.monitor.cancel()
-		self.monitor = None
-
-	def _changed(self, monitor, file1, file2, evt_type):
-		"""Change callback; something changed"""
-		if evt_type in (gio.FILE_MONITOR_EVENT_CREATED,
-				gio.FILE_MONITOR_EVENT_DELETED,
-				gio.FILE_MONITOR_EVENT_CHANGED):
-			self.mark_for_update()
-
-	def get_items(self):
-		viminfofile = os.path.expanduser(self.vim_viminfo_file)
-		if not os.path.exists(viminfofile):
-			self.output_debug("Viminfo not found at", viminfofile)
-			return
-
-		try:
-			filepaths = list(get_vim_files(viminfofile))
-		except EnvironmentError:
-			self.output_exc()
-			return
-
-		for filepath in filepaths:
-			# The most confusing glib function
-			# takes a unicode string and returns a
-			# filesystem-encoded bytestring.
-			yield FileLeaf(glib.filename_from_utf8(filepath))
-
-	def get_icon_name(self):
-		return "document-open-recent"
-
-	def provides(self):
-		yield FileLeaf
-
-
-class VimApp (AppLeaf):
-	"""
-	This is a re-implemented AppLeaf that represents a running Vim session
-
-	with a fake vim self.object for safety (this should not be needed)
-	"""
-	serializable = None
-	def __init__(self, serverid, name):
-		try:
-			obj = gio.unix.DesktopAppInfo("gvim.desktop")
-		except RuntimeError:
-			obj = gio.AppInfo(VIM)
-		Leaf.__init__(self, obj, name)
-		self.serverid = serverid
-
-	def get_id(self):
-		# use an ostensibly fake id starting with @/
-		return "@/%s/%s" % (__name__, self.serverid or "")
-
-	def __setstate__(self, state):
-		raise NotImplementedError
-
-	def __getstate__(self):
-		raise NotImplementedError
-
-	def get_actions(self):
-		if self.serverid is not None:
-			yield Launch(_("Go To"), is_running=True)
-			yield SendCommand()
-			yield CloseSaveAll()
-		else:
-			yield Launch()
-
-	def launch(self, files=(), paths=(), activate=False, ctx=None):
-		"""
-		Launch the represented application
-
-		@files: a seq of GFiles (gio.File)
-		@paths: a seq of bytestring paths
-		@activate: activate instead of start new
-		"""
-		if self.serverid is not None:
-			argv = [VIM, '--servername', self.serverid, '--remote']
-		else:
-			argv = [VIM]
-		if files:
-			paths = [f.get_path() or f.get_uri() for f in files]
-		if paths:
-			argv.extend(paths)
-		if paths or self.serverid is None:
-			try:
-				utils.spawn_async_raise(argv)
-			except utils.SpawnError as exc:
-				raise OperationError(exc)
-		if self.serverid:
-			## focus the window we opened
-			ActiveVim.vimcom.foreground(self.serverid)
-
-	def get_icon_name(self):
-		return 'vim'
-
-	def get_description(self):
-		return None
-
-class CloseSaveAll (Action):
-	""" Close a vim window without forcing """
-	rank_adjust = -5
-	def __init__(self):
-		Action.__init__(self, _("Close (Save All)"))
-
-	def activate(self, obj):
-		ActiveVim.vimcom.send_ex(obj.serverid, 'wqa')
-
-	def get_icon_name(self):
-		return "window-close"
-
-class SendCommand (Action):
-	def __init__(self):
-		Action.__init__(self, _("Send..."))
-
-	def activate(self, obj, iobj):
-		## accept with or without starting :
-		lcmd = kupferstring.tolocale(iobj.object)
-		if lcmd.startswith(":"):
-			lcmd = lcmd[1:]
-		ActiveVim.vimcom.send_ex(obj.serverid, lcmd)
-
-	def requires_object(self):
-		return True
-	def object_types(self):
-		yield TextLeaf
-	def object_source(self, for_item=None):
-		return TextSource()
-
-	def get_description(self):
-		return _("Send ex command")
-
-class InsertInVim (Action):
-	"""
-	Insert a given text into the currently open buffer in a vim
-	session
-	"""
-	def __init__(self):
-		Action.__init__(self, _("Insert in Vim..."))
-
-	def activate(self, obj, iobj):
-		tmpf, tmpname = utils.get_safe_tempfile()
-		tmpf.write(kupferstring.tolocale(obj.object))
-		tmpf.close()
-		vim_cmd = "r %s" % tmpname
-		ActiveVim.vimcom.send_ex(iobj.serverid, vim_cmd)
-		glib.timeout_add_seconds(10, os.unlink, tmpname)
-
-	def item_types(self):
-		yield TextLeaf
-
-	def requires_object(self):
-		return True
-
-	def object_types(self):
-		yield VimApp
-
-	def get_icon_name(self):
-		return "insert-text"
-
-
-class ActiveVim (Source):
-	def __init__(self):
-		Source.__init__(self, _("Active Vim Sessions"))
-
-	def initialize(self):
-		self.vimcom = vimcom.VimCom(self)
-		ActiveVim.vimcom = self.vimcom
-		self.vimcom.vim_hidden = vimcom.poller()
-		self.vimcom.stop_fetching_serverlist()
-		self.serverids = []
-		glib.timeout_add_seconds(1, self.update_serverlist)
-
-	def finalize(self):
-		pid = self.vimcom.vim_hidden.pid
-		if pid:
-			os.close(self.vimcom.vim_hidden.childfd)
-			os.kill(pid, 15)
-			os.waitpid(pid, 0)
-		self.vimcom.destroy()
-		self.vimcom = None
-		self.mark_for_update()
-
-	def get_items(self):
-		for x in self.serverids:
-			yield VimApp(x, _("Vim Session %s") % x)
-		#yield VimApp(None, _("New Vim"))
-
-	def vim_new_serverlist(self, serverlist):
-		"""this is the inaccurate serverlist"""
-		pass
-
-	def on_new_serverlist(self, new_list):
-		if set(new_list) != set(self.serverids):
-			self.serverids = new_list
-			self.mark_for_update()
-
-	def update_serverlist(self):
-		if self.vimcom:
-			self.vimcom.get_hidden_serverlist(self.on_new_serverlist)
-			return True
-
-	def provides(self):
-		yield VimApp
+def initialize_plugin(name):
+	global RecentsSource
+	global ActiveVim
+	global InsertInVim
+	from kupfer.plugin.vim.plugin import RecentsSource, ActiveVim, InsertInVim
diff --git a/kupfer/plugin/vim/plugin.py b/kupfer/plugin/vim/plugin.py
new file mode 100644
index 0000000..f168479
--- /dev/null
+++ b/kupfer/plugin/vim/plugin.py
@@ -0,0 +1,310 @@
+import os
+import sys
+
+import dbus
+import gio
+import glib
+
+from kupfer.objects import Source, FileLeaf, Leaf, Action
+from kupfer.objects import OperationError
+from kupfer.objects import AppLeaf, TextLeaf, TextSource
+from kupfer.obj.objects import Launch
+from kupfer.obj.apps import AppLeafContentMixin
+from kupfer import datatools
+from kupfer import utils
+from kupfer import kupferstring
+from kupfer import pretty
+from kupfer import plugin_support
+
+plugin_support.check_dbus_connection()
+
+PLUGID='vim'
+
+VIM = 'gvim'
+
+def get_vim_files(filepath):
+	"""
+	Read ~/.viminfo from @filepath
+
+	Look for a line like this:
+	*encoding=<encoding>
+
+	Return an iterator of unicode string file paths
+	"""
+	encoding = "UTF-8"
+	recents = []
+	with open(filepath, "r") as f:
+		for line in f:
+			if line.startswith("*encoding="):
+				_, enc = line.split("=")
+				encoding = enc.strip()
+			us_line = line.decode(encoding, "replace")
+			## Now find the jumplist
+			if us_line.startswith("-'  "):
+				parts = us_line.split(None, 3)
+				recentfile = os.path.expanduser(parts[-1].strip())
+				if recentfile:
+					recents.append(recentfile)
+	return datatools.UniqueIterator(recents)
+
+class RecentsSource (AppLeafContentMixin, Source):
+	appleaf_content_id = ("vim", "gvim")
+
+	vim_viminfo_file = "~/.viminfo"
+	def __init__(self, name=None):
+		name = name or _("Vim Recent Documents")
+		super(RecentsSource, self).__init__(name)
+
+	def initialize(self):
+		"""Set up change monitor"""
+		viminfofile = os.path.expanduser(self.vim_viminfo_file)
+		gfile = gio.File(viminfofile)
+		self.monitor = gfile.monitor_file(gio.FILE_MONITOR_NONE, None)
+		if self.monitor:
+			self.monitor.connect("changed", self._changed)
+
+	def finalize(self):
+		if self.monitor:
+			self.monitor.cancel()
+		self.monitor = None
+
+	def _changed(self, monitor, file1, file2, evt_type):
+		"""Change callback; something changed"""
+		if evt_type in (gio.FILE_MONITOR_EVENT_CREATED,
+				gio.FILE_MONITOR_EVENT_DELETED,
+				gio.FILE_MONITOR_EVENT_CHANGED):
+			self.mark_for_update()
+
+	def get_items(self):
+		viminfofile = os.path.expanduser(self.vim_viminfo_file)
+		if not os.path.exists(viminfofile):
+			self.output_debug("Viminfo not found at", viminfofile)
+			return
+
+		try:
+			filepaths = list(get_vim_files(viminfofile))
+		except EnvironmentError:
+			self.output_exc()
+			return
+
+		for filepath in filepaths:
+			# The most confusing glib function
+			# takes a unicode string and returns a
+			# filesystem-encoded bytestring.
+			yield FileLeaf(glib.filename_from_utf8(filepath))
+
+	def get_icon_name(self):
+		return "document-open-recent"
+
+	def provides(self):
+		yield FileLeaf
+
+def get_plugin_iface_name(plugin_id):
+	plugin_id = plugin_id.split(".")[-1]
+	interface_name = "se.kaizer.kupfer.plugin.%s" % plugin_id
+	return interface_name
+
+def get_plugin_service_obj(plugin_id, activate=True):
+	"""Return the dbus proxy object for our plugin
+
+	if @activate, we will --exec-helper= the service
+	"""
+	plugin_id = plugin_id.split(".")[-1]
+
+	service_name = "se.kaizer.kupfer.plugin.%s" % plugin_id
+	interface_name = "se.kaizer.kupfer.plugin.%s" % plugin_id
+	object_name = "/se/kaizer/kupfer/plugin/%s" % plugin_id
+	try:
+		bus = dbus.Bus()
+	except dbus.DBusException:
+		return None
+	try:
+		proxy_obj = bus.get_object(service_name, object_name)
+	except dbus.DBusException as exc:
+		pretty.print_debug(__name__, exc)
+		if activate:
+			return start_plugin_helper("kupfer.plugin.%s.service" % plugin_id)
+		return None
+	proxy_iface = dbus.Interface(proxy_obj, interface_name)
+	return proxy_iface
+
+def stop_plugin_service(plugin_id):
+	"""
+	Return True if it was running and was stopped
+	"""
+	plug_iface = get_plugin_service_obj(plugin_id, activate=False)
+	if plug_iface:
+		plug_iface.Exit(reply_handler=_dummy_handler)
+
+def start_plugin_helper(name):
+	argv = [sys.executable]
+	argv.extend(sys.argv)
+	argv.append('--exec-helper=%s' % name)
+	utils.spawn_async(argv)
+
+def _dummy_handler(*args):
+	pass
+
+class VimApp (AppLeaf):
+	"""
+	This is a re-implemented AppLeaf that represents a running Vim session
+
+	with a fake vim self.object for safety (this should not be needed)
+	"""
+	serializable = None
+	def __init__(self, serverid, name):
+		try:
+			obj = gio.unix.DesktopAppInfo("gvim.desktop")
+		except RuntimeError:
+			obj = gio.AppInfo(VIM)
+		Leaf.__init__(self, obj, name)
+		self.serverid = serverid
+
+	def get_id(self):
+		# use an ostensibly fake id starting with @/
+		return "@/%s/%s" % (__name__, self.serverid or "")
+
+	def __setstate__(self, state):
+		raise NotImplementedError
+
+	def __getstate__(self):
+		raise NotImplementedError
+
+	def get_actions(self):
+		if self.serverid is not None:
+			yield Launch(_("Go To"), is_running=True)
+			yield SendCommand()
+			yield CloseSaveAll()
+		else:
+			yield Launch()
+
+	def launch(self, files=(), paths=(), activate=False, ctx=None):
+		"""
+		Launch the represented application
+
+		@files: a seq of GFiles (gio.File)
+		@paths: a seq of bytestring paths
+		@activate: activate instead of start new
+		"""
+		if self.serverid is not None:
+			argv = [VIM, '--servername', self.serverid, '--remote']
+		else:
+			argv = [VIM]
+		if files:
+			paths = [f.get_path() or f.get_uri() for f in files]
+		if paths:
+			argv.extend(paths)
+		if paths or self.serverid is None:
+			try:
+				utils.spawn_async_raise(argv)
+			except utils.SpawnError as exc:
+				raise OperationError(exc)
+		if self.serverid:
+			## focus the window we opened
+			def error_handler(exc):
+				ctx.register_late_error(OperationError(exc))
+			proxy_obj = get_plugin_service_obj(PLUGID)
+			if proxy_obj:
+				proxy_obj.Foreground(self.serverid,
+						reply_handler=_dummy_handler,
+						error_handler=error_handler)
+
+	def get_icon_name(self):
+		return 'vim'
+
+	def get_description(self):
+		return None
+
+class CloseSaveAll (Action):
+	""" Close a vim window without forcing """
+	rank_adjust = -5
+	def __init__(self):
+		Action.__init__(self, _("Close (Save All)"))
+
+	def activate(self, obj):
+		ActiveVim.vimcom.send_ex(obj.serverid, 'wqa')
+
+	def get_icon_name(self):
+		return "window-close"
+
+class SendCommand (Action):
+	def __init__(self):
+		Action.__init__(self, _("Send..."))
+
+	def activate(self, obj, iobj):
+		## accept with or without starting :
+		lcmd = kupferstring.tolocale(iobj.object)
+		if lcmd.startswith(":"):
+			lcmd = lcmd[1:]
+		ActiveVim.vimcom.send_ex(obj.serverid, lcmd)
+
+	def requires_object(self):
+		return True
+	def object_types(self):
+		yield TextLeaf
+	def object_source(self, for_item=None):
+		return TextSource()
+
+	def get_description(self):
+		return _("Send ex command")
+
+class InsertInVim (Action):
+	"""
+	Insert a given text into the currently open buffer in a vim
+	session
+	"""
+	def __init__(self):
+		Action.__init__(self, _("Insert in Vim..."))
+
+	def activate(self, obj, iobj):
+		tmpf, tmpname = utils.get_safe_tempfile()
+		tmpf.write(kupferstring.tolocale(obj.object))
+		tmpf.close()
+		vim_cmd = "r %s" % tmpname
+		ActiveVim.vimcom.send_ex(iobj.serverid, vim_cmd)
+		glib.timeout_add_seconds(10, os.unlink, tmpname)
+
+	def item_types(self):
+		yield TextLeaf
+
+	def requires_object(self):
+		return True
+
+	def object_types(self):
+		yield VimApp
+
+	def get_icon_name(self):
+		return "insert-text"
+
+
+class ActiveVim (Source):
+	def __init__(self):
+		Source.__init__(self, _("Active Vim Sessions"))
+
+	def initialize(self):
+		self.serverids = []
+		glib.timeout_add_seconds(1, self.start_helper)
+
+	def start_helper(self):
+		bus = dbus.Bus()
+		bus.add_signal_receiver(self.on_new_serverlist,
+		                        signal_name="NewServerlist",
+		                        dbus_interface=get_plugin_iface_name(PLUGID),
+		                        byte_arrays=True)
+		get_plugin_service_obj(PLUGID, activate=True)
+
+	def finalize(self):
+		stop_plugin_service(PLUGID)
+
+	def get_items(self):
+		for x in self.serverids:
+			yield VimApp(x, _("Vim Session %s") % x)
+
+	def on_new_serverlist(self, new_list):
+		self.output_debug("New list:", list(new_list))
+		if set(new_list) != set(self.serverids):
+			self.serverids = map(str, new_list)
+			self.mark_for_update()
+
+	def provides(self):
+		yield VimApp
diff --git a/kupfer/plugin/vim/service.py b/kupfer/plugin/vim/service.py
new file mode 100644
index 0000000..35d2990
--- /dev/null
+++ b/kupfer/plugin/vim/service.py
@@ -0,0 +1,121 @@
+
+import os
+import sys
+
+import pygtk
+pygtk.require('2.0')
+
+import glib
+import gobject
+
+from kupfer.plugin.vim import vimcom
+
+try:
+	import dbus
+	import dbus.service
+	#import dbus.glib
+	from dbus.mainloop.glib import DBusGMainLoop
+
+except (ImportError, dbus.exceptions.DBusException) as exc:
+	print exc
+	raise SystemExit(1)
+
+PLUGID='vim'
+
+server_name = "se.kaizer.kupfer.plugin.%s" % PLUGID
+interface_name = "se.kaizer.kupfer.plugin.%s" % PLUGID
+object_name = "/se/kaizer/kupfer/plugin/%s" % PLUGID
+
+class Service (dbus.service.Object):
+	def __init__(self, mainloop, bus):
+		bus_name = dbus.service.BusName(server_name, bus=bus,
+				allow_replacement=True, replace_existing=True)
+		super(Service, self).__init__(conn=bus, object_path=object_name,
+				bus_name=bus_name)
+		self.mainloop = mainloop
+		self.initialize()
+
+	def unregister(self):
+		self.connection.release_name(server_name)
+
+	def initialize(self):
+		self.vimcom = vimcom.VimCom(self)
+		self.vimcom.vim_hidden = vimcom.poller()
+		self.vimcom.stop_fetching_serverlist()
+		self.serverids = []
+		glib.timeout_add_seconds(1, self.update_serverlist)
+
+	def finalize(self):
+		pid = self.vimcom.vim_hidden.pid
+		if pid:
+			os.close(self.vimcom.vim_hidden.childfd)
+			os.kill(pid, 15)
+			os.waitpid(pid, 0)
+		self.vimcom.destroy()
+		self.vimcom = None
+
+	def mark_for_update(self):
+		self.NewServerlist(self.serverids)
+
+	def vim_new_serverlist(self, serverlist):
+		"""this is the inaccurate serverlist"""
+		## useless callback from vimcom.VimCom
+		pass
+
+	def on_new_serverlist(self, new_list):
+		if set(new_list) != set(self.serverids):
+			self.serverids = new_list
+			self.mark_for_update()
+
+	def update_serverlist(self):
+		if self.vimcom:
+			self.vimcom.get_hidden_serverlist(self.on_new_serverlist)
+			return True
+
+	@dbus.service.method(interface_name, in_signature="ay", out_signature="b",
+	                     byte_arrays=True)
+	def Foreground(self, server):
+		if self.vimcom and server in self.serverids:
+			self.vimcom.foreground(server)
+			return True
+		return False
+
+	@dbus.service.method(interface_name, in_signature="ayay", out_signature="b",
+	                     byte_arrays=True)
+	def SendEx(self, server, excommand):
+		if self.vimcom and server in self.serverids:
+			self.vimcom.send_ex(server, excommand)
+			return True
+		return False
+
+	@dbus.service.signal(interface_name, signature="aay")
+	def NewServerlist(self, serverlist):
+		pass
+
+	@dbus.service.method(interface_name)
+	def Exit(self):
+		self.unregister()
+		self.finalize()
+		self.mainloop.quit()
+
+def start(ml):
+	#ml_wrap = DBusGMainLoop(set_as_default=True)
+	#dbus.set_default_main_loop(ml_wrap)
+	try:
+		bus = dbus.Bus()
+	except dbus.DBusException:
+		raise SystemExit(1)
+	try:
+		service = Service(ml, bus)
+	except dbus.DBusException:
+		raise SystemExit(1)
+
+def main():
+	ml_wrap = DBusGMainLoop(set_as_default=True)
+	glib.set_prgname(__name__)
+	ml = glib.MainLoop()
+	glib.idle_add(start, ml)
+	ml.run()
+
+if __name__ == '__main__':
+	main()



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]