[chrome-gnome-shell/feature/connector-update-check: 2/2] Rewritten connector to use Gio.Application/Glib.
- From: Yuri Konotopov <ykonotopov src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [chrome-gnome-shell/feature/connector-update-check: 2/2] Rewritten connector to use Gio.Application/Glib.
- Date: Sun, 25 Sep 2016 10:31:22 +0000 (UTC)
commit 974091cc4a7156f568a963f4d346afb8947d69cf
Author: Yuri Konotopov <ykonotopov gmail com>
Date: Sun Sep 25 13:28:29 2016 +0300
Rewritten connector to use Gio.Application/Glib.
connector/chrome-gnome-shell.py | 395 +++++++++++++++++++++------------------
1 files changed, 209 insertions(+), 186 deletions(-)
---
diff --git a/connector/chrome-gnome-shell.py b/connector/chrome-gnome-shell.py
index 77d8cdf..ec42bff 100755
--- a/connector/chrome-gnome-shell.py
+++ b/connector/chrome-gnome-shell.py
@@ -17,11 +17,10 @@ import json
import os
import re
import requests
+import signal
import struct
import sys
import traceback
-from select import select
-from threading import Lock
CONNECTOR_VERSION = 7.1
DEBUG_ENABLED = False
@@ -30,13 +29,32 @@ SHELL_SCHEMA = "org.gnome.shell"
ENABLED_EXTENSIONS_KEY = "enabled-extensions"
EXTENSION_DISABLE_VERSION_CHECK_KEY = "disable-extension-version-validation"
-BUFFER_SUPPORTED = hasattr(sys.stdin, 'buffer')
-mainLoop = GLib.MainLoop()
-shellAppearedId = False
-shellSignalId = False
-mutex = Lock()
+# https://developer.chrome.com/extensions/nativeMessaging#native-messaging-host-protocol
+MESSAGE_LENGTH_SIZE = 4
-proxy = Gio.DBusProxy.new_for_bus_sync(Gio.BusType.SESSION,
+# https://wiki.gnome.org/Projects/GnomeShell/Extensions/UUIDGuidelines
+def isUUID(uuid):
+ return uuid is not None and re.match('[-a-zA-Z0-9@._]+$', uuid) is not None
+
+
+def debug(message):
+ if DEBUG_ENABLED:
+ logError(message)
+
+
+def logError(message):
+ print(message, file=sys.stderr)
+
+
+class ChromeGNOMEShell(Gio.Application):
+ def __init__(self):
+ Gio.Application.__init__(self,
+ application_id='org.gnome.chrome-gnome-shell-%s' % os.getppid(),
+ flags=Gio.ApplicationFlags.HANDLES_COMMAND_LINE)
+
+ self.shellAppearedId = None
+ self.shellSignalId = None
+ self.proxy = Gio.DBusProxy.new_for_bus_sync(Gio.BusType.SESSION,
Gio.DBusProxyFlags.NONE,
None,
'org.gnome.Shell',
@@ -44,149 +62,152 @@ proxy = Gio.DBusProxy.new_for_bus_sync(Gio.BusType.SESSION,
'org.gnome.Shell.Extensions',
None)
-# https://wiki.gnome.org/Projects/GnomeShell/Extensions/UUIDGuidelines
-def isUUID(uuid):
- return uuid is not None and re.match('[-a-zA-Z0-9@._]+$', uuid) is not None
+ # Set custom exception hook
+ sys.excepthook = self.default_exception_hook
-# Helper function that sends a message to the webapp.
-def send_message(response):
- message = json.dumps(response)
- message_length = len(message.encode('utf-8'))
+ def default_exception_hook(self, type, value, tb):
+ logError("Uncaught exception of type %s occured" % type)
+ traceback.print_tb(tb)
+ logError("Exception: %s" % value)
- if message_length > 1024*1024:
- logError('Too long message (%d): "%s"' % (message_length, message))
- return
+ self.release()
- try:
- # Write message size.
- if BUFFER_SUPPORTED:
- sys.stdout.buffer.write(struct.pack(b'I', message_length))
- else:
- sys.stdout.write(struct.pack(b'I', message_length))
+ def do_startup(self):
+ debug('[%d] Startup' % (os.getpid()))
+ Gio.Application.do_startup(self)
- # Write the message itself.
- sys.stdout.write(message)
- sys.stdout.flush()
- except IOError as e:
- logError('IOError occured: %s' % e.strerror)
- sys.exit(1)
+ def do_shutdown(self):
+ debug('[%d] Shutdown' % (os.getpid()))
+ Gio.Application.do_shutdown(self)
-def send_error(message):
- send_message({'success': False, 'message': message})
+ if self.shellAppearedId:
+ Gio.bus_unwatch_name(self.shellAppearedId)
-def debug(message):
- if DEBUG_ENABLED:
- logError(message)
+ if self.shellSignalId:
+ self.proxy.disconnect(self.shellSignalId)
-def logError(message):
- print(message, file=sys.stderr)
+ def do_activate(self, app):
+ debug('[%d] Activate' % (os.getpid()))
+ Gio.Application.do_activate(self)
-def dbus_call_response(method, parameters, resultProperty):
- try:
- result = proxy.call_sync(method,
- parameters,
- Gio.DBusCallFlags.NONE,
- -1,
- None)
+ def do_local_command_line(self, arguments):
+ stdin = GLib.IOChannel.unix_new(sys.stdin.fileno())
+ stdin.set_encoding(None)
+ stdin.set_buffered(False)
- send_message({'success': True, resultProperty: result.unpack()[0]})
- except GLib.GError as e:
- send_error(e.message)
-
-def check_update(update_url):
- result = proxy.call_sync("ListExtensions",
- None,
- Gio.DBusCallFlags.NONE,
- -1,
- None)
-
- extensions = result.unpack()[0]
-
- if extensions:
- http_request = {
- 'shell_version': proxy.get_cached_property("ShellVersion").unpack(),
- 'installed': {}
- }
-
- for uuid in extensions:
- if isUUID(uuid):
- try:
- http_request['installed'][uuid] = {
- 'version': int(extensions[uuid]['version'])
- }
- except ValueError:
- http_request['installed'][uuid] = {
- 'version': 1
- }
+ GLib.unix_signal_add(GLib.PRIORITY_DEFAULT, signal.SIGINT, self.on_sigint, None)
+ GLib.io_add_watch(stdin, GLib.PRIORITY_DEFAULT, GLib.IOCondition.IN, self.on_input, None)
+ GLib.io_add_watch(stdin, GLib.PRIORITY_DEFAULT, GLib.IOCondition.HUP, self.on_hup, None)
+ GLib.io_add_watch(stdin, GLib.PRIORITY_DEFAULT, GLib.IOCondition.ERR, self.on_hup, None)
+
+ self.hold()
+
+ return (True, None, 0)
+
+
+ def on_input(self, source, condition, data):
+ debug('[%d] On input' % (os.getpid()))
+ text_length_bytes = source.read(MESSAGE_LENGTH_SIZE)
+
+ if len(text_length_bytes) == 0:
+ debug('[%d] Release condition: %s' % (os.getpid(), str(condition)))
+ self.release()
+ return
+
+ # Unpack message length as 4 byte integer.
+ text_length = struct.unpack(b'i', text_length_bytes)[0]
+
+ # Read the text (JSON object) of the message.
+ text = source.read(text_length).decode('utf-8')
+
+ request = json.loads(text)
+
+ if 'execute' in request:
+ if 'uuid' in request and not isUUID(request['uuid']):
+ return
+
+ self.process_request(request)
- http_request['installed'] = json.dumps(http_request['installed'])
+
+ def on_shell_signal(self, d_bus_proxy, sender_name, signal_name, parameters):
+ if signal_name == 'ExtensionStatusChanged':
+ debug('[%d] Signal: to %s' % (os.getpid(), signal_name))
+ self.send_message({'signal': signal_name, 'parameters': parameters.unpack()})
+ debug('[%d] Signal: from %s' % (os.getpid(), signal_name))
+
+
+ def on_shell_appeared(self, connection, name, name_owner):
+ debug('[%d] Signal: to %s' % (os.getpid(), name))
+ self.send_message({'signal': name})
+ debug('[%d] Signal: from %s' % (os.getpid(), name))
+
+
+ def on_hup(self, source, condition, data):
+ debug('[%d] On hup: %s' % (os.getpid(), str(condition)))
+ self.release()
+
+
+ def on_sigint(self, data):
+ debug('[%d] On sigint' % (os.getpid()))
+ self.release()
+
+
+ # Helper function that sends a message to the webapp.
+ def send_message(self, response):
+ message = json.dumps(response)
+ message_length = len(message.encode('utf-8'))
+
+ if message_length > 1024*1024:
+ logError('Too long message (%d): "%s"' % (message_length, message))
+ return
try:
- response = requests.get(
- update_url,
- params=http_request,
- timeout=5
- )
- response.raise_for_status()
- send_message({
- 'success': True,
- 'extensions': extensions,
- 'upgrade': response.json()}
- )
- except (
- requests.ConnectionError, requests.HTTPError, requests.Timeout,
- requests.TooManyRedirects, requests.RequestException, ValueError
- ) as ex:
- send_message({'success': False, 'message': str(ex.message) if ('message' in ex) else str(ex)})
-
-
-# Callback that reads messages from the webapp.
-def process_input(user_data):
- rlist, _, _ = select([sys.stdin], [], [], 1)
- if rlist:
- # Read the message length (first 4 bytes).
- if BUFFER_SUPPORTED:
- text_length_bytes = sys.stdin.buffer.read(4)
- else:
- text_length_bytes = sys.stdin.read(4)
- else:
- return True
-
- if len(text_length_bytes) == 0:
- mainLoop.quit()
- return False
-
- # Unpack message length as 4 byte integer.
- text_length = struct.unpack(b'i', text_length_bytes)[0]
-
- # Read the text (JSON object) of the message.
- if BUFFER_SUPPORTED:
- text = sys.stdin.buffer.read(text_length).decode('utf-8')
- else:
- text = sys.stdin.read(text_length).decode('utf-8')
-
- request = json.loads(text)
-
- if 'execute' in request:
- if 'uuid' in request and not isUUID(request['uuid']):
- return True
-
- mutex.acquire()
+ stdout = GLib.IOChannel.unix_new(sys.stdout.fileno())
+ stdout.set_encoding(None)
+ stdout.set_buffered(False)
+
+ stdout.write_chars(struct.pack(b'I', message_length), MESSAGE_LENGTH_SIZE)
+
+ # Write the message itself.
+ stdout.write_chars(message, message_length)
+ except IOError as e:
+ logError('IOError occured: %s' % e.strerror)
+ sys.exit(1)
+
+
+ def send_error(self, message):
+ self.send_message({'success': False, 'message': message})
+
+
+ def dbus_call_response(self, method, parameters, resultProperty):
+ try:
+ result = self.proxy.call_sync(method,
+ parameters,
+ Gio.DBusCallFlags.NONE,
+ -1,
+ None)
+
+ self.send_message({'success': True, resultProperty: result.unpack()[0]})
+ except GLib.GError as e:
+ send_error(e.message)
+
+
+ def process_request(self, request):
debug('[%d] Execute: to %s' % (os.getpid(), request['execute']))
if request['execute'] == 'initialize':
settings = Gio.Settings.new(SHELL_SCHEMA)
- shellVersion = proxy.get_cached_property("ShellVersion")
+ shellVersion = self.proxy.get_cached_property("ShellVersion")
if EXTENSION_DISABLE_VERSION_CHECK_KEY in settings.keys():
disableVersionCheck = settings.get_boolean(EXTENSION_DISABLE_VERSION_CHECK_KEY)
else:
disableVersionCheck = False
- send_message(
+ self.send_message(
{
'success': True,
'properties': {
@@ -198,25 +219,23 @@ def process_input(user_data):
)
elif request['execute'] == 'subscribeSignals':
- global shellAppearedId, shellSignalId
-
- if not shellAppearedId:
- shellAppearedId = Gio.bus_watch_name(Gio.BusType.SESSION,
+ if not self.shellAppearedId:
+ self.shellAppearedId = Gio.bus_watch_name(Gio.BusType.SESSION,
'org.gnome.Shell',
Gio.BusNameWatcherFlags.NONE,
- on_shell_appeared,
+ self.on_shell_appeared,
None)
- if not shellSignalId:
- shellSignalId = proxy.connect('g-signal', on_shell_signal)
+ if not self.shellSignalId:
+ self.shellSignalId = self.proxy.connect('g-signal', self.on_shell_signal)
elif request['execute'] == 'installExtension':
- dbus_call_response("InstallRemoteExtension",
+ self.dbus_call_response("InstallRemoteExtension",
GLib.Variant.new_tuple(GLib.Variant.new_string(request['uuid'])),
"status")
elif request['execute'] == 'listExtensions':
- dbus_call_response("ListExtensions", None, "extensions")
+ self.dbus_call_response("ListExtensions", None, "extensions")
elif request['execute'] == 'enableExtension':
settings = Gio.Settings.new(SHELL_SCHEMA)
@@ -239,10 +258,10 @@ def process_input(user_data):
settings.set_strv(ENABLED_EXTENSIONS_KEY, uuids)
- send_message({'success': True})
+ self.send_message({'success': True})
elif request['execute'] == 'launchExtensionPrefs':
- proxy.call("LaunchExtensionPrefs",
+ self.proxy.call("LaunchExtensionPrefs",
GLib.Variant.new_tuple(GLib.Variant.new_string(request['uuid'])),
Gio.DBusCallFlags.NONE,
-1,
@@ -251,17 +270,17 @@ def process_input(user_data):
None)
elif request['execute'] == 'getExtensionErrors':
- dbus_call_response("GetExtensionErrors",
+ self.dbus_call_response("GetExtensionErrors",
GLib.Variant.new_tuple(GLib.Variant.new_string(request['uuid'])),
"extensionErrors")
elif request['execute'] == 'getExtensionInfo':
- dbus_call_response("GetExtensionInfo",
+ self.dbus_call_response("GetExtensionInfo",
GLib.Variant.new_tuple(GLib.Variant.new_string(request['uuid'])),
"extensionInfo")
elif request['execute'] == 'uninstallExtension':
- dbus_call_response("UninstallExtension",
+ self.dbus_call_response("UninstallExtension",
GLib.Variant.new_tuple(GLib.Variant.new_string(request['uuid'])),
"status")
@@ -272,59 +291,63 @@ def process_input(user_data):
check_update(update_url)
- debug('[%d] Execute: from %s' % (os.getpid(), request['execute']))
- mutex.release()
-
- return True
-
-
-def on_shell_signal(d_bus_proxy, sender_name, signal_name, parameters):
- if signal_name == 'ExtensionStatusChanged':
- mutex.acquire()
- debug('[%d] Signal: to %s' % (os.getpid(), signal_name))
- send_message({'signal': signal_name, 'parameters': parameters.unpack()})
- debug('[%d] Signal: from %s' % (os.getpid(), signal_name))
- mutex.release()
-
-
-def on_shell_appeared(connection, name, name_owner):
- mutex.acquire()
- debug('[%d] Signal: to %s' % (os.getpid(), name))
- send_message({'signal': name})
- debug('[%d] Signal: from %s' % (os.getpid(), name))
- mutex.release()
-
-def default_exception_hook(type, value, tb):
- logError("Uncaught exception of type %s occured" % type)
- traceback.print_tb(tb)
- logError("Exception: %s" % value)
- mainLoop.quit()
+ debug('[%d] Execute: from %s' % (os.getpid(), request['execute']))
-def main():
- debug('[%d] Startup' % (os.getpid()))
-
- # Set custom exception hook
- sys.excepthook = default_exception_hook
+ def check_update(self, update_url):
+ result = self.proxy.call_sync("ListExtensions",
+ None,
+ Gio.DBusCallFlags.NONE,
+ -1,
+ None)
- GLib.idle_add(process_input, None)
+ extensions = result.unpack()[0]
+
+ if extensions:
+ http_request = {
+ 'shell_version': self.proxy.get_cached_property("ShellVersion").unpack(),
+ 'installed': {}
+ }
+
+ for uuid in extensions:
+ if isUUID(uuid):
+ try:
+ http_request['installed'][uuid] = {
+ 'version': int(extensions[uuid]['version'])
+ }
+ except ValueError:
+ http_request['installed'][uuid] = {
+ 'version': 1
+ }
+
+ http_request['installed'] = json.dumps(http_request['installed'])
+
+ try:
+ response = requests.get(
+ update_url,
+ params=http_request,
+ timeout=5
+ )
+ response.raise_for_status()
+ self.send_message({
+ 'success': True,
+ 'extensions': extensions,
+ 'upgrade': response.json()}
+ )
+ except (
+ requests.ConnectionError, requests.HTTPError, requests.Timeout,
+ requests.TooManyRedirects, requests.RequestException, ValueError
+ ) as ex:
+ self.send_message({'success': False, 'message': str(ex.message) if ('message' in ex) else
str(ex)})
- try:
- mainLoop.run()
- except KeyboardInterrupt:
- mainLoop.quit()
- if shellAppearedId:
- Gio.bus_unwatch_name(shellAppearedId)
+if __name__ == '__main__':
+ debug('[%d] Main. Use CTRL+D to quit.' % (os.getpid()))
- if shellSignalId:
- proxy.disconnect(shellSignalId)
+ app = ChromeGNOMEShell()
+ app.register()
+ app.run(sys.argv)
debug('[%d] Quit' % (os.getpid()))
- sys.exit(0)
-
-
-if __name__ == '__main__':
- main()
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]