From 1ce1c91d69805372bef9230ee527aa0e48137a14 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 06:39:23 +0200 Subject: [PATCH 01/79] app: Refactor get_all_objects. --- qutebrowser/app.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index f4e9e21c1..ed4e84eb6 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -465,17 +465,17 @@ class Application(QApplication): lines.append(repr(w)) return '\n'.join(lines) - def get_all_objects(self, obj=None, depth=0, lines=None): - """Get all children of an object recursively as a string.""" - if lines is None: - lines = [] - if obj is None: - obj = self + def _get_pyqt_objects(self, lines, obj, depth=0): + """Recursive method for get_all_objects to get Qt objects.""" for kid in obj.findChildren(QObject): lines.append(' ' * depth + repr(kid)) - self.get_all_objects(kid, depth + 1, lines) - if depth == 0: - lines.insert(0, '{} objects:'.format(len(lines))) + self._get_pyqt_objects(lines, kid, depth + 1) + + def get_all_objects(self): + """Get all children of an object recursively as a string.""" + lines = [] + self._get_pyqt_objects(lines, self) + lines.insert(0, '{} objects:'.format(len(lines))) return '\n'.join(lines) def _recover_pages(self): From 981604fc8bbeaaeb18a4f8ec6527b803b0928b2e Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 06:55:08 +0200 Subject: [PATCH 02/79] Add initial object registry. --- qutebrowser/app.py | 2 ++ qutebrowser/utils/usertypes.py | 61 ++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index ed4e84eb6..e074ca296 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -53,6 +53,7 @@ class Application(QApplication): """Main application instance. Attributes: + obj: The object registry. mainwindow: The MainWindow QWidget. debugconsole: The ConsoleWidget for debugging. commandrunner: The main CommandRunner instance. @@ -93,6 +94,7 @@ class Application(QApplication): 'tabs': False, 'main': False, } + self.obj = utypes.ObjectRegistry() self._timers = [] self._shutting_down = False self._keyparsers = None diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index e17e231ac..34a9487a8 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -381,3 +381,64 @@ class Timer(QTimer): super().start(msec) else: super().start() + + +class ObjectRegistry: + + """A registry of long-living objects in qutebrowser. + + Inspired by the eric IDE code (E5Gui/E5Application.py). + """ + + def __init__(self): + self._objects = {} + self._objects['global'] = {} + + def get(self, name, scope='global'): + """Get an object from the object registry. + + Args: + name: The name of the object to get. + scope: The scope the object is registered in. + + Return: + The registered object. + """ + try: + objects = self._objects[scope] + except KeyError: + raise KeyError("Invalid scope '{}'.".format(scope)) + try: + obj = objects[name] + except KeyError: + raise KeyError("No object '{}' in scope '{}'!".format(obj, scope)) + return obj + + def register(self, name, obj, scope='global'): + """Register an object in the object registry. + + Args: + name: The name to register the object as. + obj: The object to register. + scope: The scope to register the object in. + """ + if scope not in self._objects: + raise KeyError("Invalid scope '{}'.".format(scope)) + if name in self._objects[scope]: + existing_obj = self._objects[scope][name] + raise KeyError("Object '{}' is already registered in scope " + "'{}' ({})!".format(name, scope, existing_obj)) + self._objects[scope][name] = obj + + def dump_objects(self): + """Dump all objects as a string.""" + lines = [] + count = 0 + for scope, objects in self._objects.items(): + if objects: + lines.append("Scope {}:".format(scope)) + for name, obj in objects.items(): + lines.append(" {}: {}".format(name, repr(obj))) + count += 1 + lines.insert(0, '{} qutebrowser objects:'.format(count)) + return lines From 097645ae8e621fb4b4792703f3283cc52a3b35e4 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 06:55:29 +0200 Subject: [PATCH 03/79] app: Support object registry in get_all_objects. --- qutebrowser/app.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index e074ca296..80c77c1fe 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -475,10 +475,11 @@ class Application(QApplication): def get_all_objects(self): """Get all children of an object recursively as a string.""" - lines = [] - self._get_pyqt_objects(lines, self) - lines.insert(0, '{} objects:'.format(len(lines))) - return '\n'.join(lines) + qtb_lines = self.obj.dump_objects() + pyqt_lines = [] + self._get_pyqt_objects(pyqt_lines, self) + pyqt_lines.insert(0, '{} PyQt objects:'.format(len(pyqt_lines))) + return '\n'.join([''] + qtb_lines + [''] + pyqt_lines) def _recover_pages(self): """Try to recover all open pages. From 2694ab2e80af7fa81dea95daa20fa39badca3424 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 07:03:18 +0200 Subject: [PATCH 04/79] app: Make commandrunner/debugconsole private. --- qutebrowser/app.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 80c77c1fe..b88745ba3 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -55,8 +55,6 @@ class Application(QApplication): Attributes: obj: The object registry. mainwindow: The MainWindow QWidget. - debugconsole: The ConsoleWidget for debugging. - commandrunner: The main CommandRunner instance. searchrunner: The main SearchRunner instance. config: The main ConfigManager stateconfig: The "state" ReadWriteConfigParser instance. @@ -67,6 +65,8 @@ class Application(QApplication): cache: The global DiskCache instance. rl_bridge: The ReadlineBridge being used. args: ArgumentParser instance. + _commandrunner: The main CommandRunner instance. + _debugconsole: The ConsoleWidget for debugging. _keyparsers: A mapping from modes to keyparsers. _timers: List of used QTimers so they don't get GCed. _shutting_down: True if we're currently shutting down. @@ -135,7 +135,7 @@ class Application(QApplication): log.init.debug("Initializing cache...") self.cache = cache.DiskCache(self) log.init.debug("Initializing commands...") - self.commandrunner = runners.CommandRunner() + self._commandrunner = runners.CommandRunner() log.init.debug("Initializing search...") self.searchrunner = runners.SearchRunner(self) log.init.debug("Initializing downloads...") @@ -144,7 +144,7 @@ class Application(QApplication): self.mainwindow = mainwindow.MainWindow() self.modeman.mainwindow = self.mainwindow log.init.debug("Initializing debug console...") - self.debugconsole = console.ConsoleWidget() + self._debugconsole = console.ConsoleWidget() log.init.debug("Initializing eventfilter...") self.installEventFilter(self.modeman) self.setQuitOnLastWindowClosed(False) @@ -334,7 +334,7 @@ class Application(QApplication): for cmd in self.args.command: if cmd.startswith(':'): log.init.debug("Startup cmd {}".format(cmd)) - self.commandrunner.run_safely_init(cmd.lstrip(':')) + self._commandrunner.run_safely_init(cmd.lstrip(':')) else: log.init.debug("Startup URL {}".format(cmd)) try: @@ -391,14 +391,14 @@ class Application(QApplication): self.modeman.left.connect(status.prompt.prompter.on_mode_left) # commands - cmd.got_cmd.connect(self.commandrunner.run_safely) + cmd.got_cmd.connect(self._commandrunner.run_safely) cmd.got_search.connect(self.searchrunner.search) cmd.got_search_rev.connect(self.searchrunner.search_rev) cmd.returnPressed.connect(tabs.setFocus) self.searchrunner.do_search.connect(tabs.search) kp[utypes.KeyMode.normal].keystring_updated.connect( status.keystring.setText) - tabs.got_cmd.connect(self.commandrunner.run_safely) + tabs.got_cmd.connect(self._commandrunner.run_safely) # hints kp[utypes.KeyMode.hint].fire_hint.connect(tabs.fire_hint) @@ -664,7 +664,7 @@ class Application(QApplication): @cmdutils.register(instance='', debug=True, name='debug-console') def show_debugconsole(self): """Show the debugging console.""" - self.debugconsole.show() + self._debugconsole.show() def interrupt(self, signum, _frame): """Handler for signals to gracefully shutdown (SIGINT/SIGTERM). From 3b3675d1afcc77803b62dbe3c062f14f1cdadd42 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 07:31:54 +0200 Subject: [PATCH 05/79] utils: Add helper functions get_object/register_object. --- qutebrowser/utils/utils.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index 387c53d90..2df4aa889 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -578,3 +578,13 @@ def is_enum(obj): return issubclass(obj, enum.Enum) except TypeError: return False + + +def get_object(name, scope='global'): + """Helper function to get an object.""" + return QCoreApplication.instance().obj.get(name, scope) + + +def register_object(name, obj, scope='global'): + """Helper function to register an object.""" + return QCoreApplication.instance().obj.register(name, obj, scope) From ca2be960dfc8f303d45f3daf6f6baa687acabe2d Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 07:32:14 +0200 Subject: [PATCH 06/79] Use object registry for cache/cookiejar. --- qutebrowser/app.py | 16 ++++++++++------ qutebrowser/network/networkmanager.py | 16 ++++++++++------ qutebrowser/utils/usertypes.py | 4 ++++ 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index b88745ba3..8f08e4c84 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -61,8 +61,6 @@ class Application(QApplication): cmd_history: The "cmd_history" LineConfigParser instance. messagebridge: The global MessageBridge instance. modeman: The global ModeManager instance. - cookiejar: The global CookieJar instance. - cache: The global DiskCache instance. rl_bridge: The ReadlineBridge being used. args: ArgumentParser instance. _commandrunner: The main CommandRunner instance. @@ -131,9 +129,11 @@ class Application(QApplication): log.init.debug("Initializing utility commands...") utilcmds.init() log.init.debug("Initializing cookies...") - self.cookiejar = cookies.CookieJar(self) + cookiejar = cookies.CookieJar(self) + self.obj.register('cookiejar', cookiejar) log.init.debug("Initializing cache...") - self.cache = cache.DiskCache(self) + diskcache = cache.DiskCache(self) + self.obj.register('cache', diskcache) log.init.debug("Initializing commands...") self._commandrunner = runners.CommandRunner() log.init.debug("Initializing search...") @@ -756,8 +756,12 @@ class Application(QApplication): to_save.append(("command history", self.cmd_history.save)) if hasattr(self, 'stateconfig'): to_save.append(("window geometry", self.stateconfig.save)) - if hasattr(self, 'cookiejar'): - to_save.append(("cookies", self.cookiejar.save)) + try: + cookiejar = self.obj.get('cookiejar') + except KeyError: + pass + else: + to_save.append(("cookies", cookiejar.save)) for what, handler in to_save: log.destroy.debug("Saving {} (handler: {})".format( what, handler.__qualname__)) diff --git a/qutebrowser/network/networkmanager.py b/qutebrowser/network/networkmanager.py index 33852fffd..bb7eaa708 100644 --- a/qutebrowser/network/networkmanager.py +++ b/qutebrowser/network/networkmanager.py @@ -51,13 +51,17 @@ class NetworkManager(QNetworkAccessManager): self._scheme_handlers = { 'qute': qutescheme.QuteSchemeHandler(), } + + # We have a shared cookie jar and cache - we restore their parents so + # we don't take ownership of them. app = QCoreApplication.instance() - self.setCookieJar(app.cookiejar) - self.setCache(app.cache) - # We have a shared cookie jar and cache , so we don't want the - # NetworkManager to take ownership of them. - app.cookiejar.setParent(app) - app.cache.setParent(app) + cookiejar = utils.get_object('cookiejar') + self.setCookieJar(cookiejar) + cookiejar.setParent(app) + cache = utils.get_object('cache') + self.setCache(cache) + cache.setParent(app) + if SSL_AVAILABLE: self.sslErrors.connect(self.on_ssl_errors) self.authenticationRequired.connect(self.on_authentication_required) diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index 34a9487a8..7f91d1fe5 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -388,6 +388,10 @@ class ObjectRegistry: """A registry of long-living objects in qutebrowser. Inspired by the eric IDE code (E5Gui/E5Application.py). + + Objects registered globally: + cookiejar: CookieJar instance. + cache: DiskCache instance. """ def __init__(self): From dc7ad3e2de8ae4f67d76dd58df444c6e059a598b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 07:36:47 +0200 Subject: [PATCH 07/79] Use a normal UserDict for ObjectRegistry. --- qutebrowser/app.py | 6 ++-- qutebrowser/utils/usertypes.py | 54 +++++++--------------------------- qutebrowser/utils/utils.py | 8 ++--- 3 files changed, 17 insertions(+), 51 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 8f08e4c84..311d981bb 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -130,10 +130,10 @@ class Application(QApplication): utilcmds.init() log.init.debug("Initializing cookies...") cookiejar = cookies.CookieJar(self) - self.obj.register('cookiejar', cookiejar) + self.obj['cookiejar'] = cookiejar log.init.debug("Initializing cache...") diskcache = cache.DiskCache(self) - self.obj.register('cache', diskcache) + self.obj['cache'] = diskcache log.init.debug("Initializing commands...") self._commandrunner = runners.CommandRunner() log.init.debug("Initializing search...") @@ -757,7 +757,7 @@ class Application(QApplication): if hasattr(self, 'stateconfig'): to_save.append(("window geometry", self.stateconfig.save)) try: - cookiejar = self.obj.get('cookiejar') + cookiejar = self.obj['cookiejar'] except KeyError: pass else: diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index 7f91d1fe5..13163c741 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -24,6 +24,7 @@ Module attributes: """ import operator +import collections import collections.abc import enum as pyenum @@ -383,7 +384,7 @@ class Timer(QTimer): super().start() -class ObjectRegistry: +class ObjectRegistry(collections.UserDict): """A registry of long-living objects in qutebrowser. @@ -394,55 +395,20 @@ class ObjectRegistry: cache: DiskCache instance. """ - def __init__(self): - self._objects = {} - self._objects['global'] = {} - - def get(self, name, scope='global'): - """Get an object from the object registry. - - Args: - name: The name of the object to get. - scope: The scope the object is registered in. - - Return: - The registered object. - """ - try: - objects = self._objects[scope] - except KeyError: - raise KeyError("Invalid scope '{}'.".format(scope)) - try: - obj = objects[name] - except KeyError: - raise KeyError("No object '{}' in scope '{}'!".format(obj, scope)) - return obj - - def register(self, name, obj, scope='global'): + def __setitem__(self, name, obj): """Register an object in the object registry. - Args: - name: The name to register the object as. - obj: The object to register. - scope: The scope to register the object in. + Prevents duplicated registrations. """ - if scope not in self._objects: - raise KeyError("Invalid scope '{}'.".format(scope)) - if name in self._objects[scope]: - existing_obj = self._objects[scope][name] + if name in self.data: raise KeyError("Object '{}' is already registered in scope " - "'{}' ({})!".format(name, scope, existing_obj)) - self._objects[scope][name] = obj + "'{}' ({})!".format(name, scope, self.data[name])) + super().__setitem__(name, obj) def dump_objects(self): """Dump all objects as a string.""" lines = [] - count = 0 - for scope, objects in self._objects.items(): - if objects: - lines.append("Scope {}:".format(scope)) - for name, obj in objects.items(): - lines.append(" {}: {}".format(name, repr(obj))) - count += 1 - lines.insert(0, '{} qutebrowser objects:'.format(count)) + for name, obj in self.data.items(): + lines.append(" {}: {}".format(name, repr(obj))) + lines.insert(0, '{} objects:'.format(len(self.data))) return lines diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index 2df4aa889..6f51febfe 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -580,11 +580,11 @@ def is_enum(obj): return False -def get_object(name, scope='global'): +def get_object(name): """Helper function to get an object.""" - return QCoreApplication.instance().obj.get(name, scope) + return QCoreApplication.instance().obj[name] -def register_object(name, obj, scope='global'): +def register_object(name, obj): """Helper function to register an object.""" - return QCoreApplication.instance().obj.register(name, obj, scope) + QCoreApplication.instance().obj[name] = obj From dce5289b69e84f72a51aa7d0dd346e2dd3c13f2f Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 07:38:14 +0200 Subject: [PATCH 08/79] Rename global registry from obj to registry. --- qutebrowser/app.py | 12 ++++++------ qutebrowser/utils/utils.py | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 311d981bb..4cdf51b71 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -53,7 +53,7 @@ class Application(QApplication): """Main application instance. Attributes: - obj: The object registry. + registry: The object registry. mainwindow: The MainWindow QWidget. searchrunner: The main SearchRunner instance. config: The main ConfigManager @@ -92,7 +92,7 @@ class Application(QApplication): 'tabs': False, 'main': False, } - self.obj = utypes.ObjectRegistry() + self.registry = utypes.ObjectRegistry() self._timers = [] self._shutting_down = False self._keyparsers = None @@ -130,10 +130,10 @@ class Application(QApplication): utilcmds.init() log.init.debug("Initializing cookies...") cookiejar = cookies.CookieJar(self) - self.obj['cookiejar'] = cookiejar + self.registry['cookiejar'] = cookiejar log.init.debug("Initializing cache...") diskcache = cache.DiskCache(self) - self.obj['cache'] = diskcache + self.registry['cache'] = diskcache log.init.debug("Initializing commands...") self._commandrunner = runners.CommandRunner() log.init.debug("Initializing search...") @@ -475,7 +475,7 @@ class Application(QApplication): def get_all_objects(self): """Get all children of an object recursively as a string.""" - qtb_lines = self.obj.dump_objects() + qtb_lines = self.registry.dump_objects() pyqt_lines = [] self._get_pyqt_objects(pyqt_lines, self) pyqt_lines.insert(0, '{} PyQt objects:'.format(len(pyqt_lines))) @@ -757,7 +757,7 @@ class Application(QApplication): if hasattr(self, 'stateconfig'): to_save.append(("window geometry", self.stateconfig.save)) try: - cookiejar = self.obj['cookiejar'] + cookiejar = self.registry['cookiejar'] except KeyError: pass else: diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index 6f51febfe..912d8f952 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -582,9 +582,9 @@ def is_enum(obj): def get_object(name): """Helper function to get an object.""" - return QCoreApplication.instance().obj[name] + return QCoreApplication.instance().registry[name] def register_object(name, obj): """Helper function to register an object.""" - QCoreApplication.instance().obj[name] = obj + QCoreApplication.instance().registry[name] = obj From 9e5d8b2480af35baf3de0b17a91cd1f82d24f309 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 07:43:02 +0200 Subject: [PATCH 09/79] Add a meta-registry. --- qutebrowser/app.py | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 4cdf51b71..63cefc539 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -53,7 +53,8 @@ class Application(QApplication): """Main application instance. Attributes: - registry: The object registry. + registry: The object registry of global objects. + meta_registry: The object registry of object registries. mainwindow: The MainWindow QWidget. searchrunner: The main SearchRunner instance. config: The main ConfigManager @@ -92,7 +93,9 @@ class Application(QApplication): 'tabs': False, 'main': False, } + self.meta_registry = utypes.ObjectRegistry() self.registry = utypes.ObjectRegistry() + self.meta_registry['global'] = self.registry self._timers = [] self._shutting_down = False self._keyparsers = None @@ -473,9 +476,21 @@ class Application(QApplication): lines.append(' ' * depth + repr(kid)) self._get_pyqt_objects(lines, kid, depth + 1) + def _get_registered_objects(self): + """Get all registered objects in all registries as a string.""" + blocks = [] + lines = [] + for name, registry in self.meta_registry.items(): + blocks.append((name, registry.dump_objects())) + for name, data in blocks: + lines.append("'{}' object registry:".format(name)) + for line in data: + lines.append(" {}".format(line)) + return lines + def get_all_objects(self): """Get all children of an object recursively as a string.""" - qtb_lines = self.registry.dump_objects() + qtb_lines = self._get_registered_objects() pyqt_lines = [] self._get_pyqt_objects(pyqt_lines, self) pyqt_lines.insert(0, '{} PyQt objects:'.format(len(pyqt_lines))) From 30209f531efb0d899f96711d8db50b88d892a5c3 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 07:48:34 +0200 Subject: [PATCH 10/79] Improve get_all_objects output. --- qutebrowser/app.py | 14 ++++++++++---- qutebrowser/utils/usertypes.py | 5 ++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 63cefc539..26dcd90f5 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -483,18 +483,24 @@ class Application(QApplication): for name, registry in self.meta_registry.items(): blocks.append((name, registry.dump_objects())) for name, data in blocks: - lines.append("'{}' object registry:".format(name)) + lines.append("{} object registry - {} objects:".format( + name, len(data))) for line in data: lines.append(" {}".format(line)) return lines def get_all_objects(self): """Get all children of an object recursively as a string.""" - qtb_lines = self._get_registered_objects() + output = [''] + output += self._get_registered_objects() + output += [''] pyqt_lines = [] self._get_pyqt_objects(pyqt_lines, self) - pyqt_lines.insert(0, '{} PyQt objects:'.format(len(pyqt_lines))) - return '\n'.join([''] + qtb_lines + [''] + pyqt_lines) + pyqt_lines = [' ' + e for e in pyqt_lines] + pyqt_lines.insert(0, 'Qt objects - {} objects:'.format( + len(pyqt_lines))) + output += pyqt_lines + return '\n'.join(output) def _recover_pages(self): """Try to recover all open pages. diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index 13163c741..6062a7ad8 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -406,9 +406,8 @@ class ObjectRegistry(collections.UserDict): super().__setitem__(name, obj) def dump_objects(self): - """Dump all objects as a string.""" + """Dump all objects as a list of strings.""" lines = [] for name, obj in self.data.items(): - lines.append(" {}: {}".format(name, repr(obj))) - lines.insert(0, '{} objects:'.format(len(self.data))) + lines.append("{}: {}".format(name, repr(obj))) return lines From aa681f5ad2f1f898f0ebc948824a829c165aa628 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 07:53:40 +0200 Subject: [PATCH 11/79] Merge get_all_widgets into get_all_objects. --- qutebrowser/app.py | 25 ++++++++++--------------- qutebrowser/utils/utilcmds.py | 7 ------- qutebrowser/widgets/crash.py | 10 ++-------- 3 files changed, 12 insertions(+), 30 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 26dcd90f5..5031dfcd8 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -460,15 +460,11 @@ class Application(QApplication): tabs.start_download.connect(self.downloadmanager.fetch) tabs.download_get.connect(self.downloadmanager.get) - def get_all_widgets(self): + def _get_widgets(self): """Get a string list of all widgets.""" - lines = [] widgets = self.allWidgets() widgets.sort(key=lambda e: repr(e)) - lines.append("{} widgets".format(len(widgets))) - for w in widgets: - lines.append(repr(w)) - return '\n'.join(lines) + return [repr(w) for w in widgets] def _get_pyqt_objects(self, lines, obj, depth=0): """Recursive method for get_all_objects to get Qt objects.""" @@ -500,6 +496,12 @@ class Application(QApplication): pyqt_lines.insert(0, 'Qt objects - {} objects:'.format( len(pyqt_lines))) output += pyqt_lines + output += [''] + widget_lines = self._get_widgets() + widget_lines = [' ' + e for e in widget_lines] + widget_lines.insert(0, "Qt widgets - {} objects".format( + len(widget_lines))) + output += widget_lines return '\n'.join(output) def _recover_pages(self): @@ -587,12 +589,6 @@ class Application(QApplication): log.destroy.exception("Error while getting history: {}") history = [] - try: - widgets = self.get_all_widgets() - except Exception: - log.destroy.exception("Error while getting widgets") - widgets = "" - try: objects = self.get_all_objects() except Exception: @@ -605,7 +601,7 @@ class Application(QApplication): log.destroy.exception("Error while preventing shutdown") QApplication.closeAllWindows() self._crashdlg = crash.ExceptionCrashDialog(pages, history, exc, - widgets, objects) + objects) ret = self._crashdlg.exec_() if ret == QDialog.Accepted: # restore self.restart(shutdown=False, pages=pages) @@ -677,9 +673,8 @@ class Application(QApplication): """Report a bug in qutebrowser.""" pages = self._recover_pages() history = self.mainwindow.status.cmd.history[-5:] - widgets = self.get_all_widgets() objects = self.get_all_objects() - self._crashdlg = crash.ReportDialog(pages, history, widgets, objects) + self._crashdlg = crash.ReportDialog(pages, history, objects) self._crashdlg.show() @cmdutils.register(instance='', debug=True, name='debug-console') diff --git a/qutebrowser/utils/utilcmds.py b/qutebrowser/utils/utilcmds.py index b2565306a..faaf8dbab 100644 --- a/qutebrowser/utils/utilcmds.py +++ b/qutebrowser/utils/utilcmds.py @@ -86,13 +86,6 @@ def debug_crash(typ: ('exception', 'segfault')='exception'): raise Exception("Forced crash") -@cmdutils.register(debug=True) -def debug_all_widgets(): - """Print a list of all widgets to debug log.""" - s = QCoreApplication.instance().get_all_widgets() - log.misc.debug(s) - - @cmdutils.register(debug=True) def debug_all_objects(): """Print a list of all objects to the debug log.""" diff --git a/qutebrowser/widgets/crash.py b/qutebrowser/widgets/crash.py index a78ff52d1..1a76da689 100644 --- a/qutebrowser/widgets/crash.py +++ b/qutebrowser/widgets/crash.py @@ -176,18 +176,16 @@ class ExceptionCrashDialog(_CrashDialog): _pages: A list of the open pages (URLs as strings) _cmdhist: A list with the command history (as strings) _exc: An exception tuple (type, value, traceback) - _widgets: A list of active widgets as string. _objects: A list of all QObjects as string. """ - def __init__(self, pages, cmdhist, exc, widgets, objects, parent=None): + def __init__(self, pages, cmdhist, exc, objects, parent=None): self._pages = pages self._cmdhist = cmdhist self._exc = exc self._btn_quit = None self._btn_restore = None self._btn_pastebin = None - self._widgets = widgets self._objects = objects super().__init__(parent) self.setModal(True) @@ -225,7 +223,6 @@ class ExceptionCrashDialog(_CrashDialog): ("Commandline args", ' '.join(sys.argv[1:])), ("Open Pages", '\n'.join(self._pages)), ("Command history", '\n'.join(self._cmdhist)), - ("Widgets", self._widgets), ("Objects", self._objects), ] try: @@ -285,16 +282,14 @@ class ReportDialog(_CrashDialog): _btn_pastebin: The pastebin button. _pages: A list of the open pages (URLs as strings) _cmdhist: A list with the command history (as strings) - _widgets: A list of active widgets as string. _objects: A list of all QObjects as string. """ - def __init__(self, pages, cmdhist, widgets, objects, parent=None): + def __init__(self, pages, cmdhist, objects, parent=None): self._pages = pages self._cmdhist = cmdhist self._btn_ok = None self._btn_pastebin = None - self._widgets = widgets self._objects = objects super().__init__(parent) self.setAttribute(Qt.WA_DeleteOnClose) @@ -324,7 +319,6 @@ class ReportDialog(_CrashDialog): ("Commandline args", ' '.join(sys.argv[1:])), ("Open Pages", '\n'.join(self._pages)), ("Command history", '\n'.join(self._cmdhist)), - ("Widgets", self._widgets), ("Objects", self._objects), ] try: From 10f3c47ae2593b72c3684fe6e341bf7880e05c2f Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 18:59:41 +0200 Subject: [PATCH 12/79] Add __repr__ for cookies/cache. --- qutebrowser/browser/cache.py | 5 +++++ qutebrowser/browser/cookies.py | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/qutebrowser/browser/cache.py b/qutebrowser/browser/cache.py index 994e1dba0..42d379e75 100644 --- a/qutebrowser/browser/cache.py +++ b/qutebrowser/browser/cache.py @@ -37,3 +37,8 @@ class DiskCache(QNetworkDiskCache): cache_dir = utils.get_standard_dir(QStandardPaths.CacheLocation) self.setCacheDirectory(os.path.join(cache_dir, 'http')) self.setMaximumCacheSize(config.get('storage', 'cache-size')) + + def __repr__(self): + return '<{} size={}, maxsize={}, path={}>'.format( + self.__class__.__name__, self.cacheSize(), self.maximumCacheSize(), + self.cacheDirectory()) diff --git a/qutebrowser/browser/cookies.py b/qutebrowser/browser/cookies.py index a8b1ed64a..992764c6a 100644 --- a/qutebrowser/browser/cookies.py +++ b/qutebrowser/browser/cookies.py @@ -40,6 +40,10 @@ class CookieJar(QNetworkCookieJar): cookies += QNetworkCookie.parseCookies(line) self.setAllCookies(cookies) + def __repr__(self): + return '<{} count={}>'.format( + self.__class__.__name__, len(self.allCookies())) + def purge_old_cookies(self): """Purge expired cookies from the cookie jar.""" # Based on: From 10eb849ae71d08159e666db6c6085c1ba6592e4e Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 19:03:36 +0200 Subject: [PATCH 13/79] Fix lint --- qutebrowser/app.py | 2 +- qutebrowser/utils/usertypes.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 5031dfcd8..0c3ecfe6c 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -747,7 +747,7 @@ class Application(QApplication): # event loop, so we can shut down immediately. self._shutdown(status) - def _shutdown(self, status): # noqa + def _shutdown(self, status): # noqa, pylint: disable=too-many-branches """Second stage of shutdown.""" log.destroy.debug("Stage 2 of shutting down...") # Remove eventfilter diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index 6062a7ad8..520b0db2b 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -401,8 +401,8 @@ class ObjectRegistry(collections.UserDict): Prevents duplicated registrations. """ if name in self.data: - raise KeyError("Object '{}' is already registered in scope " - "'{}' ({})!".format(name, scope, self.data[name])) + raise KeyError("Object '{}' is already registered ({})!".format( + name, repr(self.data[name]))) super().__setitem__(name, obj) def dump_objects(self): From ab95234dada67dcda6030980d3302c00f67cbd10 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 19:08:39 +0200 Subject: [PATCH 14/79] Add object registry support to command handler. --- qutebrowser/commands/command.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/qutebrowser/commands/command.py b/qutebrowser/commands/command.py index dbf8bbb90..e16c40b93 100644 --- a/qutebrowser/commands/command.py +++ b/qutebrowser/commands/command.py @@ -304,7 +304,11 @@ class Command: app = QCoreApplication.instance() if self.instance == '': obj = app + elif self.instance in app.registry: + # Use object registry where available + obj = app.registry[self.instance] else: + # FIXME remove this obj = utils.dotted_getattr(app, self.instance) args.append(obj) From 68cfe499fca54b421f4435f25bedb8886fb67ae5 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 19:08:56 +0200 Subject: [PATCH 15/79] Use object registry for rl_bridge. --- qutebrowser/app.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 0c3ecfe6c..c8d5bb338 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -62,7 +62,6 @@ class Application(QApplication): cmd_history: The "cmd_history" LineConfigParser instance. messagebridge: The global MessageBridge instance. modeman: The global ModeManager instance. - rl_bridge: The ReadlineBridge being used. args: ArgumentParser instance. _commandrunner: The main CommandRunner instance. _debugconsole: The ConsoleWidget for debugging. @@ -101,7 +100,6 @@ class Application(QApplication): self._keyparsers = None self._crashdlg = None self._crashlogfile = None - self.rl_bridge = None self.messagebridge = None self.stateconfig = None self.modeman = None @@ -270,7 +268,8 @@ class Application(QApplication): self.setApplicationName("qutebrowser") self.setApplicationVersion(qutebrowser.__version__) self.messagebridge = message.MessageBridge(self) - self.rl_bridge = readline.ReadlineBridge() + rl_bridge = readline.ReadlineBridge() + self.registry['rl_bridge'] = rl_bridge def _handle_segfault(self): """Handle a segfault from a previous run.""" From 21bdf517b78c228e51a316f15569d8b3d136615b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 19:10:24 +0200 Subject: [PATCH 16/79] Use object registry for searchrunner. --- qutebrowser/app.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index c8d5bb338..f73300b3f 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -56,7 +56,6 @@ class Application(QApplication): registry: The object registry of global objects. meta_registry: The object registry of object registries. mainwindow: The MainWindow QWidget. - searchrunner: The main SearchRunner instance. config: The main ConfigManager stateconfig: The "state" ReadWriteConfigParser instance. cmd_history: The "cmd_history" LineConfigParser instance. @@ -138,7 +137,8 @@ class Application(QApplication): log.init.debug("Initializing commands...") self._commandrunner = runners.CommandRunner() log.init.debug("Initializing search...") - self.searchrunner = runners.SearchRunner(self) + searchrunner = runners.SearchRunner(self) + self.registry['searchrunner'] = searchrunner log.init.debug("Initializing downloads...") self.downloadmanager = downloads.DownloadManager(self) log.init.debug("Initializing main window...") @@ -381,6 +381,7 @@ class Application(QApplication): tabs = self.mainwindow.tabs cmd = self.mainwindow.status.cmd completer = self.mainwindow.completion.completer + searchrunner = self.registry['searchrunner'] # misc self.lastWindowClosed.connect(self.shutdown) @@ -394,10 +395,10 @@ class Application(QApplication): # commands cmd.got_cmd.connect(self._commandrunner.run_safely) - cmd.got_search.connect(self.searchrunner.search) - cmd.got_search_rev.connect(self.searchrunner.search_rev) + cmd.got_search.connect(searchrunner.search) + cmd.got_search_rev.connect(searchrunner.search_rev) cmd.returnPressed.connect(tabs.setFocus) - self.searchrunner.do_search.connect(tabs.search) + searchrunner.do_search.connect(tabs.search) kp[utypes.KeyMode.normal].keystring_updated.connect( status.keystring.setText) tabs.got_cmd.connect(self._commandrunner.run_safely) From 1d535ae3009d892f7e13bfd4f0d3978b3872ff27 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 19:22:55 +0200 Subject: [PATCH 17/79] Use object registry for stateconfig. --- qutebrowser/app.py | 18 +++++++++++------- qutebrowser/widgets/mainwindow.py | 6 +++--- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index f73300b3f..221580ff7 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -57,7 +57,6 @@ class Application(QApplication): meta_registry: The object registry of object registries. mainwindow: The MainWindow QWidget. config: The main ConfigManager - stateconfig: The "state" ReadWriteConfigParser instance. cmd_history: The "cmd_history" LineConfigParser instance. messagebridge: The global MessageBridge instance. modeman: The global ModeManager instance. @@ -100,7 +99,6 @@ class Application(QApplication): self._crashdlg = None self._crashlogfile = None self.messagebridge = None - self.stateconfig = None self.modeman = None self.cmd_history = None self.config = None @@ -211,7 +209,8 @@ class Application(QApplication): msgbox.exec_() # We didn't really initialize much so far, so we just quit hard. sys.exit(1) - self.stateconfig = iniparsers.ReadWriteConfigParser(confdir, 'state') + stateconfig = iniparsers.ReadWriteConfigParser(confdir, 'state') + self.registry['stateconfig'] = stateconfig self.cmd_history = lineparser.LineConfigParser( confdir, 'cmd_history', ('completion', 'history-length')) @@ -529,13 +528,14 @@ class Application(QApplication): def _save_geometry(self): """Save the window geometry to the state config.""" + stateconfig = self.registry['stateconfig'] data = bytes(self.mainwindow.saveGeometry()) geom = base64.b64encode(data).decode('ASCII') try: - self.stateconfig.add_section('geometry') + stateconfig.add_section('geometry') except configparser.DuplicateSectionError: pass - self.stateconfig['geometry']['mainwindow'] = geom + stateconfig['geometry']['mainwindow'] = geom def _destroy_crashlogfile(self): """Clean up the crash log file and delete it.""" @@ -770,8 +770,12 @@ class Application(QApplication): ("quickmarks", quickmarks.save)] if hasattr(self, 'cmd_history'): to_save.append(("command history", self.cmd_history.save)) - if hasattr(self, 'stateconfig'): - to_save.append(("window geometry", self.stateconfig.save)) + try: + stateconfig = self.registry['stateconfig'] + except KeyError: + pass + else: + to_save.append(("window geometry", stateconfig.save)) try: cookiejar = self.registry['cookiejar'] except KeyError: diff --git a/qutebrowser/widgets/mainwindow.py b/qutebrowser/widgets/mainwindow.py index 0d265fd23..cf41e21fc 100644 --- a/qutebrowser/widgets/mainwindow.py +++ b/qutebrowser/widgets/mainwindow.py @@ -22,12 +22,12 @@ import binascii import base64 -from PyQt5.QtCore import pyqtSlot, QRect, QPoint, QCoreApplication, QTimer +from PyQt5.QtCore import pyqtSlot, QRect, QPoint, QTimer from PyQt5.QtWidgets import QWidget, QVBoxLayout from qutebrowser.commands import cmdutils from qutebrowser.config import config -from qutebrowser.utils import message, log, usertypes, qtutils +from qutebrowser.utils import message, log, usertypes, qtutils, utils from qutebrowser.widgets import tabbedbrowser, completion, downloads from qutebrowser.widgets.statusbar import bar @@ -50,8 +50,8 @@ class MainWindow(QWidget): super().__init__(parent) self.setWindowTitle('qutebrowser') + stateconf = utils.get_object('stateconfig') try: - stateconf = QCoreApplication.instance().stateconfig data = stateconf['geometry']['mainwindow'] log.init.debug("Restoring mainwindow from {}".format(data)) geom = base64.b64decode(data, validate=True) From 58be65f4f0a472c194dc5dd614afab8e12b5d805 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 19:31:18 +0200 Subject: [PATCH 18/79] Use object registry for messagebridge. --- qutebrowser/app.py | 21 ++++++++++----------- qutebrowser/browser/downloads.py | 2 +- qutebrowser/browser/hints.py | 6 +++--- qutebrowser/utils/message.py | 21 ++++++++------------- 4 files changed, 22 insertions(+), 28 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 221580ff7..88524d414 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -58,7 +58,6 @@ class Application(QApplication): mainwindow: The MainWindow QWidget. config: The main ConfigManager cmd_history: The "cmd_history" LineConfigParser instance. - messagebridge: The global MessageBridge instance. modeman: The global ModeManager instance. args: ArgumentParser instance. _commandrunner: The main CommandRunner instance. @@ -98,7 +97,6 @@ class Application(QApplication): self._keyparsers = None self._crashdlg = None self._crashlogfile = None - self.messagebridge = None self.modeman = None self.cmd_history = None self.config = None @@ -266,7 +264,8 @@ class Application(QApplication): self.setOrganizationName("qutebrowser") self.setApplicationName("qutebrowser") self.setApplicationVersion(qutebrowser.__version__) - self.messagebridge = message.MessageBridge(self) + messagebridge = message.MessageBridge(self) + self.registry['messagebridge'] = messagebridge rl_bridge = readline.ReadlineBridge() self.registry['rl_bridge'] = rl_bridge @@ -381,6 +380,7 @@ class Application(QApplication): cmd = self.mainwindow.status.cmd completer = self.mainwindow.completion.completer searchrunner = self.registry['searchrunner'] + messagebridge = self.registry['messagebridge'] # misc self.lastWindowClosed.connect(self.shutdown) @@ -410,14 +410,13 @@ class Application(QApplication): kp[utypes.KeyMode.hint].on_hint_strings_updated) # messages - self.messagebridge.s_error.connect(status.disp_error) - self.messagebridge.s_info.connect(status.disp_temp_text) - self.messagebridge.s_set_text.connect(status.set_text) - self.messagebridge.s_maybe_reset_text.connect( - status.txt.maybe_reset_text) - self.messagebridge.s_set_cmd_text.connect(cmd.set_cmd_text) - self.messagebridge.s_question.connect( - status.prompt.prompter.ask_question, Qt.DirectConnection) + messagebridge.s_error.connect(status.disp_error) + messagebridge.s_info.connect(status.disp_temp_text) + messagebridge.s_set_text.connect(status.set_text) + messagebridge.s_maybe_reset_text.connect(status.txt.maybe_reset_text) + messagebridge.s_set_cmd_text.connect(cmd.set_cmd_text) + messagebridge.s_question.connect(status.prompt.prompter.ask_question, + Qt.DirectConnection) # config self.config.style_changed.connect(style.get_stylesheet.cache_clear) diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py index 33eccc146..b481956a5 100644 --- a/qutebrowser/browser/downloads.py +++ b/qutebrowser/browser/downloads.py @@ -409,7 +409,7 @@ class DownloadManager(QObject): q.destroyed.connect(functools.partial(self.questions.remove, q)) self.questions.append(q) download.cancelled.connect(q.abort) - message.instance().ask(q, blocking=False) + utils.get_object('message').ask(q, blocking=False) @pyqtSlot(DownloadItem) def on_finished(self, download): diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 4556a25a4..16cb21fb7 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -31,7 +31,7 @@ from qutebrowser.config import config from qutebrowser.keyinput import modeman from qutebrowser.browser import webelem from qutebrowser.commands import userscripts, cmdexc -from qutebrowser.utils import usertypes, log, qtutils, message +from qutebrowser.utils import usertypes, log, qtutils, message, utils ElemTuple = collections.namedtuple('ElemTuple', 'elem, label') @@ -160,7 +160,7 @@ class HintManager(QObject): except webelem.IsNullError: pass text = self.HINT_TEXTS[self._context.target] - message.instance().maybe_reset_text(text) + utils.get_object('messagebridge').maybe_reset_text(text) self._context = None def _hint_strings(self, elems): @@ -541,7 +541,7 @@ class HintManager(QObject): self._context.frames = webelem.get_child_frames(mainframe) self._context.args = args self._init_elements(mainframe, group) - message.instance().set_text(self.HINT_TEXTS[target]) + utils.get_object('messagebridge').set_text(self.HINT_TEXTS[target]) self._connect_frame_signals() try: modeman.enter(usertypes.KeyMode.hint, 'HintManager.start') diff --git a/qutebrowser/utils/message.py b/qutebrowser/utils/message.py index f1fa9221c..22b1d86e0 100644 --- a/qutebrowser/utils/message.py +++ b/qutebrowser/utils/message.py @@ -21,12 +21,7 @@ from PyQt5.QtCore import pyqtSignal, QCoreApplication, QObject, QTimer -from qutebrowser.utils import usertypes, log - - -def instance(): - """Get the global messagebridge instance.""" - return QCoreApplication.instance().messagebridge +from qutebrowser.utils import usertypes, log, utils def error(message, immediately=False): @@ -35,7 +30,7 @@ def error(message, immediately=False): Args: See MessageBridge.error. """ - instance().error(message, immediately) + utils.get_object('messagebridge').error(message, immediately) def info(message, immediately=True): @@ -44,12 +39,12 @@ def info(message, immediately=True): Args: See MessageBridge.info. """ - instance().info(message, immediately) + utils.get_object('messagebridge').info(message, immediately) def set_cmd_text(txt): """Convienience function to Set the statusbar command line to a text.""" - instance().set_cmd_text(txt) + utils.get_object('messagebridge').set_cmd_text(txt) def ask(message, mode, default=None): @@ -67,7 +62,7 @@ def ask(message, mode, default=None): q.text = message q.mode = mode q.default = default - instance().ask(q, blocking=True) + utils.get_object('messagebridge').ask(q, blocking=True) q.deleteLater() return q.answer @@ -77,7 +72,7 @@ def alert(message): q = usertypes.Question() q.text = message q.mode = usertypes.PromptMode.alert - instance().ask(q, blocking=True) + utils.get_object('messagebridge').ask(q, blocking=True) q.deleteLater() @@ -92,7 +87,7 @@ def ask_async(message, mode, handler, default=None): """ if not isinstance(mode, usertypes.PromptMode): raise TypeError("Mode {} is no PromptMode member!".format(mode)) - bridge = instance() + bridge = utils.get_object('messagebridge') q = usertypes.Question(bridge) q.text = message q.mode = mode @@ -111,7 +106,7 @@ def confirm_async(message, yes_action, no_action=None, default=None): no_action: Callable to be called when the user answered no. default: True/False to set a default value, or None. """ - bridge = instance() + bridge = utils.get_object('messagebridge') q = usertypes.Question(bridge) q.text = message q.mode = usertypes.PromptMode.yesno From 184babbd846d1d38caa9401c360ed528e1ded7b0 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 19:46:44 +0200 Subject: [PATCH 19/79] Remove destroyed QObjects from ObjectRegistry. --- qutebrowser/utils/usertypes.py | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index 520b0db2b..947ea22a2 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -24,6 +24,7 @@ Module attributes: """ import operator +import functools import collections import collections.abc import enum as pyenum @@ -398,13 +399,23 @@ class ObjectRegistry(collections.UserDict): def __setitem__(self, name, obj): """Register an object in the object registry. - Prevents duplicated registrations. + Prevents duplicated registrations and sets a slot to remove QObjects + when they are destroyed. """ if name in self.data: raise KeyError("Object '{}' is already registered ({})!".format( name, repr(self.data[name]))) + if isinstance(obj, QObject): + obj.destroyed.connect(functools.partial(self.on_destroyed, name)) super().__setitem__(name, obj) + def on_destroyed(self, name): + """Remove a destroyed QObject.""" + try: + del self[name] + except KeyError: + pass + def dump_objects(self): """Dump all objects as a list of strings.""" lines = [] From d441471a1706d572dc6a9f94280124027a00c6f6 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 19:47:02 +0200 Subject: [PATCH 20/79] Use object registry for app._timers. --- qutebrowser/app.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 88524d414..1280c4cd1 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -63,7 +63,6 @@ class Application(QApplication): _commandrunner: The main CommandRunner instance. _debugconsole: The ConsoleWidget for debugging. _keyparsers: A mapping from modes to keyparsers. - _timers: List of used QTimers so they don't get GCed. _shutting_down: True if we're currently shutting down. _quit_status: The current quitting status. _crashdlg: The crash dialog currently open. @@ -92,7 +91,6 @@ class Application(QApplication): self.meta_registry = utypes.ObjectRegistry() self.registry = utypes.ObjectRegistry() self.meta_registry['global'] = self.registry - self._timers = [] self._shutting_down = False self._keyparsers = None self._crashdlg = None @@ -155,8 +153,7 @@ class Application(QApplication): self.mainwindow.show() log.init.debug("Applying python hacks...") self._python_hacks() - timer = QTimer.singleShot(0, self._process_init_args) - self._timers.append(timer) + QTimer.singleShot(0, self._process_init_args) log.init.debug("Init done!") @@ -367,7 +364,7 @@ class Application(QApplication): timer = utypes.Timer(self, 'python_hacks') timer.start(500) timer.timeout.connect(lambda: None) - self._timers.append(timer) + self.registry['python_hack_timer'] = timer def _connect_signals(self): """Connect all signals to their slots.""" From 5f45d3de6091ee0625b79ad2a848829315847ab8 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 19:57:51 +0200 Subject: [PATCH 21/79] Use object registry for modeman. --- qutebrowser/app.py | 63 ++++++++++++----------- qutebrowser/browser/hints.py | 4 +- qutebrowser/commands/command.py | 4 +- qutebrowser/keyinput/modeman.py | 15 ++---- qutebrowser/widgets/statusbar/bar.py | 6 +-- qutebrowser/widgets/statusbar/prompter.py | 5 +- qutebrowser/widgets/webview.py | 3 +- 7 files changed, 49 insertions(+), 51 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 1280c4cd1..1064c758f 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -58,7 +58,6 @@ class Application(QApplication): mainwindow: The MainWindow QWidget. config: The main ConfigManager cmd_history: The "cmd_history" LineConfigParser instance. - modeman: The global ModeManager instance. args: ArgumentParser instance. _commandrunner: The main CommandRunner instance. _debugconsole: The ConsoleWidget for debugging. @@ -95,7 +94,6 @@ class Application(QApplication): self._keyparsers = None self._crashdlg = None self._crashlogfile = None - self.modeman = None self.cmd_history = None self.config = None self.keyconfig = None @@ -112,6 +110,7 @@ class Application(QApplication): self._handle_segfault() log.init.debug("Initializing modes...") self._init_modes() + modeman_obj = self.registry['modeman'] log.init.debug("Initializing websettings...") websettings.init() log.init.debug("Initializing quickmarks...") @@ -137,16 +136,16 @@ class Application(QApplication): self.downloadmanager = downloads.DownloadManager(self) log.init.debug("Initializing main window...") self.mainwindow = mainwindow.MainWindow() - self.modeman.mainwindow = self.mainwindow + modeman_obj.mainwindow = self.mainwindow log.init.debug("Initializing debug console...") self._debugconsole = console.ConsoleWidget() log.init.debug("Initializing eventfilter...") - self.installEventFilter(self.modeman) + self.installEventFilter(modeman_obj) self.setQuitOnLastWindowClosed(False) log.init.debug("Connecting signals...") self._connect_signals() - self.modeman.enter(utypes.KeyMode.normal, 'init') + modeman.enter(utypes.KeyMode.normal, 'init') log.init.debug("Showing mainwindow...") if not args.nowindow: @@ -227,26 +226,27 @@ class Application(QApplication): utypes.KeyMode.yesno: modeparsers.PromptKeyParser(self), } - self.modeman = modeman.ModeManager(self) - self.modeman.register(utypes.KeyMode.normal, - self._keyparsers[utypes.KeyMode.normal].handle) - self.modeman.register(utypes.KeyMode.hint, - self._keyparsers[utypes.KeyMode.hint].handle) - self.modeman.register(utypes.KeyMode.insert, - self._keyparsers[utypes.KeyMode.insert].handle, - passthrough=True) - self.modeman.register( + modeman_obj = modeman.ModeManager(self) + self.registry['modeman'] = modeman_obj + modeman_obj.register(utypes.KeyMode.normal, + self._keyparsers[utypes.KeyMode.normal].handle) + modeman_obj.register(utypes.KeyMode.hint, + self._keyparsers[utypes.KeyMode.hint].handle) + modeman_obj.register(utypes.KeyMode.insert, + self._keyparsers[utypes.KeyMode.insert].handle, + passthrough=True) + modeman_obj.register( utypes.KeyMode.passthrough, self._keyparsers[utypes.KeyMode.passthrough].handle, passthrough=True) - self.modeman.register(utypes.KeyMode.command, - self._keyparsers[utypes.KeyMode.command].handle, - passthrough=True) - self.modeman.register(utypes.KeyMode.prompt, - self._keyparsers[utypes.KeyMode.prompt].handle, - passthrough=True) - self.modeman.register(utypes.KeyMode.yesno, - self._keyparsers[utypes.KeyMode.yesno].handle) + modeman_obj.register(utypes.KeyMode.command, + self._keyparsers[utypes.KeyMode.command].handle, + passthrough=True) + modeman_obj.register(utypes.KeyMode.prompt, + self._keyparsers[utypes.KeyMode.prompt].handle, + passthrough=True) + modeman_obj.register(utypes.KeyMode.yesno, + self._keyparsers[utypes.KeyMode.yesno].handle) def _init_misc(self): """Initialize misc things.""" @@ -378,16 +378,17 @@ class Application(QApplication): completer = self.mainwindow.completion.completer searchrunner = self.registry['searchrunner'] messagebridge = self.registry['messagebridge'] + modeman = self.registry['modeman'] # misc self.lastWindowClosed.connect(self.shutdown) tabs.quit.connect(self.shutdown) # status bar - self.modeman.entered.connect(status.on_mode_entered) - self.modeman.left.connect(status.on_mode_left) - self.modeman.left.connect(status.cmd.on_mode_left) - self.modeman.left.connect(status.prompt.prompter.on_mode_left) + modeman.entered.connect(status.on_mode_entered) + modeman.left.connect(status.on_mode_left) + modeman.left.connect(status.cmd.on_mode_left) + modeman.left.connect(status.prompt.prompter.on_mode_left) # commands cmd.got_cmd.connect(self._commandrunner.run_safely) @@ -418,7 +419,7 @@ class Application(QApplication): # config self.config.style_changed.connect(style.get_stylesheet.cache_clear) for obj in (tabs, completion, self.mainwindow, self.cmd_history, - websettings, self.modeman, status, status.txt): + websettings, modeman, status, status.txt): self.config.changed.connect(obj.on_config_changed) for obj in kp.values(): self.keyconfig.changed.connect(obj.on_keyconfig_changed) @@ -444,7 +445,7 @@ class Application(QApplication): tabs.cur_load_status_changed.connect(status.url.on_load_status_changed) # command input / completion - self.modeman.left.connect(tabs.on_mode_left) + modeman.left.connect(tabs.on_mode_left) cmd.clear_completion_selection.connect( completion.on_clear_completion_selection) cmd.hide_completion.connect(completion.hide) @@ -747,9 +748,11 @@ class Application(QApplication): """Second stage of shutdown.""" log.destroy.debug("Stage 2 of shutting down...") # Remove eventfilter - if self.modeman is not None: + try: log.destroy.debug("Removing eventfilter...") - self.removeEventFilter(self.modeman) + self.removeEventFilter(self.registry['modeman']) + except KeyError: + pass # Close all tabs if self.mainwindow is not None: log.destroy.debug("Closing tabs...") diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 16cb21fb7..b4430c063 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -149,8 +149,8 @@ class HintManager(QObject): """ super().__init__(parent) self._context = None - modeman.instance().left.connect(self.on_mode_left) - modeman.instance().entered.connect(self.on_mode_entered) + utils.get_object('modeman').left.connect(self.on_mode_left) + utils.get_object('modeman').entered.connect(self.on_mode_entered) def _cleanup(self): """Clean up after hinting.""" diff --git a/qutebrowser/commands/command.py b/qutebrowser/commands/command.py index e16c40b93..4a13e97bd 100644 --- a/qutebrowser/commands/command.py +++ b/qutebrowser/commands/command.py @@ -98,9 +98,7 @@ class Command: Raise: PrerequisitesError if the command can't be called currently. """ - # We don't use modeman.instance() here to avoid a circular import - # of qutebrowser.keyinput.modeman. - curmode = QCoreApplication.instance().modeman.mode() + curmode = utils.get_object('modeman').mode() if self.modes is not None and curmode not in self.modes: mode_names = '/'.join(mode.name for mode in self.modes) raise cmdexc.PrerequisitesError( diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index 297621b2e..394c07388 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -29,7 +29,7 @@ from PyQt5.QtWidgets import QApplication from qutebrowser.config import config from qutebrowser.commands import cmdexc, cmdutils -from qutebrowser.utils import usertypes, log +from qutebrowser.utils import usertypes, log, utils class ModeLockedError(Exception): @@ -37,25 +37,20 @@ class ModeLockedError(Exception): """Exception raised when the mode is currently locked.""" -def instance(): - """Get the global modeman instance.""" - return QApplication.instance().modeman - - def enter(mode, reason=None): """Enter the mode 'mode'.""" - instance().enter(mode, reason) + utils.get_object('modeman').enter(mode, reason) def leave(mode, reason=None): """Leave the mode 'mode'.""" - instance().leave(mode, reason) + utils.get_object('modeman').leave(mode, reason) def maybe_enter(mode, reason=None): """Convenience method to enter 'mode' without exceptions.""" try: - instance().enter(mode, reason) + utils.get_object('modeman').enter(mode, reason) except ModeLockedError: pass @@ -63,7 +58,7 @@ def maybe_enter(mode, reason=None): def maybe_leave(mode, reason=None): """Convenience method to leave 'mode' without exceptions.""" try: - instance().leave(mode, reason) + utils.get_object('modeman').leave(mode, reason) except ValueError as e: # This is rather likely to happen, so we only log to debug log. log.modes.debug(e) diff --git a/qutebrowser/widgets/statusbar/bar.py b/qutebrowser/widgets/statusbar/bar.py index 44e514d8a..968d4ec0c 100644 --- a/qutebrowser/widgets/statusbar/bar.py +++ b/qutebrowser/widgets/statusbar/bar.py @@ -27,7 +27,7 @@ from PyQt5.QtWidgets import QWidget, QHBoxLayout, QStackedLayout, QSizePolicy from qutebrowser.keyinput import modeman from qutebrowser.config import config, style -from qutebrowser.utils import usertypes, log +from qutebrowser.utils import usertypes, log, utils from qutebrowser.widgets.statusbar import (command, progress, keystring, percentage, url, prompt) from qutebrowser.widgets.statusbar import text as textwidget @@ -376,7 +376,7 @@ class StatusBar(QWidget): @pyqtSlot(usertypes.KeyMode) def on_mode_entered(self, mode): """Mark certain modes in the commandline.""" - if mode in modeman.instance().passthrough: + if mode in utils.get_object('modeman').passthrough: text = "-- {} MODE --".format(mode.name.upper()) self.txt.set_text(self.txt.Text.normal, text) if mode == usertypes.KeyMode.insert: @@ -385,7 +385,7 @@ class StatusBar(QWidget): @pyqtSlot(usertypes.KeyMode) def on_mode_left(self, mode): """Clear marked mode.""" - if mode in modeman.instance().passthrough: + if mode in utils.get_object('modeman').passthrough: self.txt.set_text(self.txt.Text.normal, '') if mode == usertypes.KeyMode.insert: self._set_insert_active(False) diff --git a/qutebrowser/widgets/statusbar/prompter.py b/qutebrowser/widgets/statusbar/prompter.py index e0d13ddba..ec62551ad 100644 --- a/qutebrowser/widgets/statusbar/prompter.py +++ b/qutebrowser/widgets/statusbar/prompter.py @@ -280,13 +280,14 @@ class Prompter: self.question = question mode = self._display_question() question.aborted.connect(lambda: modeman.maybe_leave(mode, 'aborted')) + modeman_obj = utils.get_object('modeman') try: modeman.enter(mode, 'question asked') except modeman.ModeLockedError: - if modeman.instance().mode() != usertypes.KeyMode.prompt: + if modeman_obj.mode() != usertypes.KeyMode.prompt: question.abort() return None - modeman.instance().locked = True + modeman_obj.locked = True if blocking: loop = qtutils.EventLoop() self._loops.append(loop) diff --git a/qutebrowser/widgets/webview.py b/qutebrowser/widgets/webview.py index 2229cb625..2f42754d8 100644 --- a/qutebrowser/widgets/webview.py +++ b/qutebrowser/widgets/webview.py @@ -358,7 +358,8 @@ class WebView(QWebView): self._set_load_status(LoadStatus.error) if not config.get('input', 'auto-insert-mode'): return - if modeman.instance().mode() == usertypes.KeyMode.insert or not ok: + if (utils.get_object('modeman').mode() == usertypes.KeyMode.insert or + not ok): return frame = self.page().currentFrame() try: From f5b1d3ce4d34f33c6a0742da2f5b5443f5386525 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 21:35:08 +0200 Subject: [PATCH 22/79] Use object registry for command-dispatcher. --- qutebrowser/browser/commands.py | 68 ++++++++++++++-------------- qutebrowser/widgets/tabbedbrowser.py | 4 +- 2 files changed, 36 insertions(+), 36 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index aeae7c4ff..f3bbf0a1c 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -155,7 +155,7 @@ class CommandDispatcher: except PermissionError: raise cmdexc.CommandError("Failed to delete tempfile...") - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def tab_close(self, count=None): """Close the current/[count]th tab. @@ -171,7 +171,7 @@ class CommandDispatcher: return self._tabs.close_tab(tab) - @cmdutils.register(instance='mainwindow.tabs.cmd', name='open', + @cmdutils.register(instance='command-dispatcher', name='open', split=False) def openurl(self, url, bg=False, tab=False, count=None): """Open a URL in the current/[count]th tab. @@ -203,7 +203,7 @@ class CommandDispatcher: else: curtab.openurl(url) - @cmdutils.register(instance='mainwindow.tabs.cmd', name='reload') + @cmdutils.register(instance='command-dispatcher', name='reload') def reloadpage(self, count=None): """Reload the current/[count]th tab. @@ -214,7 +214,7 @@ class CommandDispatcher: if tab is not None: tab.reload() - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def stop(self, count=None): """Stop loading in the current/[count]th tab. @@ -225,7 +225,7 @@ class CommandDispatcher: if tab is not None: tab.stop() - @cmdutils.register(instance='mainwindow.tabs.cmd', name='print') + @cmdutils.register(instance='command-dispatcher', name='print') def printpage(self, preview=False, count=None): """Print the current/[count]th tab. @@ -249,7 +249,7 @@ class CommandDispatcher: diag.setAttribute(Qt.WA_DeleteOnClose) diag.open(lambda: tab.print(diag.printer())) - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def back(self, count=1): """Go back in the history of the current tab. @@ -259,7 +259,7 @@ class CommandDispatcher: for _ in range(count): self._current_widget().go_back() - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def forward(self, count=1): """Go forward in the history of the current tab. @@ -269,7 +269,7 @@ class CommandDispatcher: for _ in range(count): self._current_widget().go_forward() - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def hint(self, group=webelem.Group.all, target=hints.Target.normal, *args: {'nargs': '*'}): """Start hinting. @@ -313,7 +313,7 @@ class CommandDispatcher: widget.hintmanager.start(frame, self._tabs.current_url(), group, target, *args) - @cmdutils.register(instance='mainwindow.tabs.cmd', hide=True) + @cmdutils.register(instance='command-dispatcher', hide=True) def follow_hint(self): """Follow the currently selected hint.""" self._current_widget().hintmanager.follow_hint() @@ -365,7 +365,7 @@ class CommandDispatcher: url.setPath(new_path) self._open(url, tab, background=False) - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def navigate(self, where: ('prev', 'next', 'up', 'increment', 'decrement'), tab=False): """Open typical prev/next links or navigate using the URL path. @@ -405,7 +405,7 @@ class CommandDispatcher: raise ValueError("Got called with invalid value {} for " "`where'.".format(where)) - @cmdutils.register(instance='mainwindow.tabs.cmd', hide=True) + @cmdutils.register(instance='command-dispatcher', hide=True) def scroll(self, dx: float, dy: float, count=1): """Scroll the current tab by 'count * dx/dy'. @@ -420,7 +420,7 @@ class CommandDispatcher: cmdutils.check_overflow(dy, 'int') self._current_widget().page().currentFrame().scroll(dx, dy) - @cmdutils.register(instance='mainwindow.tabs.cmd', hide=True) + @cmdutils.register(instance='command-dispatcher', hide=True) def scroll_perc(self, perc: float=None, horizontal: {'flag': 'x'}=False, count=None): """Scroll to a specific percentage of the page. @@ -436,7 +436,7 @@ class CommandDispatcher: self._scroll_percent(perc, count, Qt.Horizontal if horizontal else Qt.Vertical) - @cmdutils.register(instance='mainwindow.tabs.cmd', hide=True) + @cmdutils.register(instance='command-dispatcher', hide=True) def scroll_page(self, x: float, y: float, count=1): """Scroll the frame page-wise. @@ -453,7 +453,7 @@ class CommandDispatcher: cmdutils.check_overflow(dy, 'int') frame.scroll(dx, dy) - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def yank(self, title=False, sel=False): """Yank the current URL/title to the clipboard or primary selection. @@ -478,7 +478,7 @@ class CommandDispatcher: what = 'Title' if title else 'URL' message.info("{} yanked to {}".format(what, target)) - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def zoom_in(self, count=1): """Increase the zoom level for the current tab. @@ -488,7 +488,7 @@ class CommandDispatcher: tab = self._current_widget() tab.zoom(count) - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def zoom_out(self, count=1): """Decrease the zoom level for the current tab. @@ -498,7 +498,7 @@ class CommandDispatcher: tab = self._current_widget() tab.zoom(-count) - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def zoom(self, zoom=None, count=None): """Set the zoom level for the current tab. @@ -516,7 +516,7 @@ class CommandDispatcher: tab = self._current_widget() tab.zoom_perc(level) - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def tab_only(self): """Close all tabs except for the current one.""" for tab in self._tabs.widgets(): @@ -524,7 +524,7 @@ class CommandDispatcher: continue self._tabs.close_tab(tab) - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def undo(self): """Re-open a closed tab (optionally skipping [count] closed tabs).""" if self._tabs.url_stack: @@ -532,7 +532,7 @@ class CommandDispatcher: else: raise cmdexc.CommandError("Nothing to undo!") - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def tab_prev(self, count=1): """Switch to the previous tab, or switch [count] tabs back. @@ -547,7 +547,7 @@ class CommandDispatcher: else: raise cmdexc.CommandError("First tab") - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def tab_next(self, count=1): """Switch to the next tab, or switch [count] tabs forward. @@ -562,7 +562,7 @@ class CommandDispatcher: else: raise cmdexc.CommandError("Last tab") - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def paste(self, sel=False, tab=False, bg=False): """Open a page from the clipboard. @@ -588,7 +588,7 @@ class CommandDispatcher: raise cmdexc.CommandError(e) self._open(url, tab, bg) - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def tab_focus(self, index: (int, 'last')=None, count=None): """Select the tab given as argument/[count]. @@ -612,7 +612,7 @@ class CommandDispatcher: raise cmdexc.CommandError("There's no tab with index {}!".format( idx)) - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def tab_move(self, direction: ('+', '-')=None, count=None): """Move the current tab. @@ -650,7 +650,7 @@ class CommandDispatcher: finally: self._tabs.setUpdatesEnabled(True) - @cmdutils.register(instance='mainwindow.tabs.cmd', split=False) + @cmdutils.register(instance='command-dispatcher', split=False) def spawn(self, *args): """Spawn a command in a shell. @@ -668,12 +668,12 @@ class CommandDispatcher: log.procs.debug("Executing: {}".format(args)) subprocess.Popen(args) - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def home(self): """Open main startpage in current tab.""" self.openurl(config.get('general', 'startpage')[0]) - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def run_userscript(self, cmd, *args: {'nargs': '*'}): """Run an userscript given as argument. @@ -684,12 +684,12 @@ class CommandDispatcher: url = self._tabs.current_url() userscripts.run(cmd, *args, url=url) - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def quickmark_save(self): """Save the current page as a quickmark.""" quickmarks.prompt_save(self._tabs.current_url()) - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def quickmark_load(self, name, tab=False, bg=False): """Load a quickmark. @@ -705,7 +705,7 @@ class CommandDispatcher: urlstr, url.errorString())) self._open(url, tab, bg) - @cmdutils.register(instance='mainwindow.tabs.cmd', name='inspector') + @cmdutils.register(instance='command-dispatcher', name='inspector') def toggle_inspector(self): """Toggle the web inspector.""" cur = self._current_widget() @@ -727,13 +727,13 @@ class CommandDispatcher: else: cur.inspector.show() - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def download_page(self): """Download the current page.""" page = self._current_widget().page() self._tabs.download_get.emit(self._tabs.current_url(), page) - @cmdutils.register(instance='mainwindow.tabs.cmd') + @cmdutils.register(instance='command-dispatcher') def view_source(self): """Show the source of the current page.""" # pylint doesn't seem to like pygments... @@ -752,7 +752,7 @@ class CommandDispatcher: tab.setHtml(highlighted, url) tab.viewing_source = True - @cmdutils.register(instance='mainwindow.tabs.cmd', name='help', + @cmdutils.register(instance='command-dispatcher', name='help', completion=[usertypes.Completion.helptopic]) def show_help(self, topic=None): r"""Show help about a command or setting. @@ -789,7 +789,7 @@ class CommandDispatcher: raise cmdexc.CommandError("Invalid help topic {}!".format(topic)) self.openurl('qute://help/{}'.format(path)) - @cmdutils.register(instance='mainwindow.tabs.cmd', + @cmdutils.register(instance='command-dispatcher', modes=[usertypes.KeyMode.insert], hide=True) def open_editor(self): diff --git a/qutebrowser/widgets/tabbedbrowser.py b/qutebrowser/widgets/tabbedbrowser.py index 2a6294077..eae530e2b 100644 --- a/qutebrowser/widgets/tabbedbrowser.py +++ b/qutebrowser/widgets/tabbedbrowser.py @@ -54,7 +54,6 @@ class TabbedBrowser(tabwidget.TabWidget): tabbar -> new-tab-position set to 'left'. _tab_insert_idx_right: Same as above, for 'right'. url_stack: Stack of URLs of closed tabs. - cmd: A TabCommandDispatcher instance. last_focused: The tab which was focused last. Signals: @@ -110,7 +109,8 @@ class TabbedBrowser(tabwidget.TabWidget): self._tabs = [] self.url_stack = [] self._filter = signalfilter.SignalFilter(self) - self.cmd = commands.CommandDispatcher(self) + dispatcher = commands.CommandDispatcher(self) + utils.register_object('command-dispatcher', dispatcher) self.last_focused = None self._now_focused = None # FIXME adjust this to font size From 9a3ceebf2e1d264d9c163781452156131571162c Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 21:39:42 +0200 Subject: [PATCH 23/79] Use object registry for completer. --- qutebrowser/app.py | 2 +- qutebrowser/widgets/completion.py | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 1064c758f..64717b43e 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -375,7 +375,7 @@ class Application(QApplication): completion = self.mainwindow.completion tabs = self.mainwindow.tabs cmd = self.mainwindow.status.cmd - completer = self.mainwindow.completion.completer + completer = self.registry['completer'] searchrunner = self.registry['searchrunner'] messagebridge = self.registry['messagebridge'] modeman = self.registry['modeman'] diff --git a/qutebrowser/widgets/completion.py b/qutebrowser/widgets/completion.py index 9594d800c..12e2dc6b5 100644 --- a/qutebrowser/widgets/completion.py +++ b/qutebrowser/widgets/completion.py @@ -29,7 +29,7 @@ from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QItemSelectionModel from qutebrowser.commands import cmdutils from qutebrowser.config import config, style from qutebrowser.widgets import completiondelegate -from qutebrowser.utils import completer, usertypes, qtutils +from qutebrowser.utils import completer, usertypes, qtutils, utils class CompletionView(QTreeView): @@ -45,7 +45,6 @@ class CompletionView(QTreeView): COLUMN_WIDTHS: A list of column widths, in percent. Attributes: - completer: The Completer instance to use. enabled: Whether showing the CompletionView is enabled. _height: The height to use for the CompletionView. _height_perc: Either None or a percentage if height should be relative. @@ -92,7 +91,8 @@ class CompletionView(QTreeView): def __init__(self, parent=None): super().__init__(parent) - self.completer = completer.Completer(self) + completer_obj = completer.Completer(self) + utils.register_object('completer', completer_obj) self.enabled = config.get('completion', 'show') self._delegate = completiondelegate.CompletionItemDelegate(self) @@ -225,7 +225,7 @@ class CompletionView(QTreeView): def selectionChanged(self, selected, deselected): """Extend selectionChanged to call completers selection_changed.""" super().selectionChanged(selected, deselected) - self.completer.selection_changed(selected, deselected) + utils.get_object('completer').selection_changed(selected, deselected) def resizeEvent(self, e): """Extend resizeEvent to adjust column size.""" From 40812f81b6805ec1396942a1c94b641d85d1e486 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 21:46:38 +0200 Subject: [PATCH 24/79] Use object registry for status-cmd. --- qutebrowser/app.py | 8 ++++---- qutebrowser/widgets/statusbar/bar.py | 13 +++++++------ qutebrowser/widgets/statusbar/command.py | 8 ++++---- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 64717b43e..c9b521f50 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -374,7 +374,7 @@ class Application(QApplication): status = self.mainwindow.status completion = self.mainwindow.completion tabs = self.mainwindow.tabs - cmd = self.mainwindow.status.cmd + cmd = self.registry['status-cmd'] completer = self.registry['completer'] searchrunner = self.registry['searchrunner'] messagebridge = self.registry['messagebridge'] @@ -387,7 +387,7 @@ class Application(QApplication): # status bar modeman.entered.connect(status.on_mode_entered) modeman.left.connect(status.on_mode_left) - modeman.left.connect(status.cmd.on_mode_left) + modeman.left.connect(cmd.on_mode_left) modeman.left.connect(status.prompt.prompter.on_mode_left) # commands @@ -581,7 +581,7 @@ class Application(QApplication): pages = [] try: - history = self.mainwindow.status.cmd.history[-5:] + history = self.registry['status-cmd'].history[-5:] except Exception: log.destroy.exception("Error while getting history: {}") history = [] @@ -669,7 +669,7 @@ class Application(QApplication): def report(self): """Report a bug in qutebrowser.""" pages = self._recover_pages() - history = self.mainwindow.status.cmd.history[-5:] + history = self.registry['status-cmd'].history[-5:] objects = self.get_all_objects() self._crashdlg = crash.ReportDialog(pages, history, objects) self._crashdlg.show() diff --git a/qutebrowser/widgets/statusbar/bar.py b/qutebrowser/widgets/statusbar/bar.py index 968d4ec0c..08c23e213 100644 --- a/qutebrowser/widgets/statusbar/bar.py +++ b/qutebrowser/widgets/statusbar/bar.py @@ -41,12 +41,12 @@ class StatusBar(QWidget): """The statusbar at the bottom of the mainwindow. Attributes: - cmd: The Command widget in the statusbar. txt: The Text widget in the statusbar. keystring: The KeyString widget in the statusbar. percentage: The Percentage widget in the statusbar. url: The UrlText widget in the statusbar. prog: The Progress widget in the statusbar. + _cmd: The Command widget in the statusbar. _hbox: The main QHBoxLayout. _stack: The QStackedLayout with cmd/txt widgets. _text_queue: A deque of (error, text) tuples to be displayed. @@ -132,8 +132,9 @@ class StatusBar(QWidget): self._hbox.addLayout(self._stack) self._stack.setContentsMargins(0, 0, 0, 0) - self.cmd = command.Command() - self._stack.addWidget(self.cmd) + self._cmd = command.Command() + utils.register_object('status-cmd', self._cmd) + self._stack.addWidget(self._cmd) self.txt = textwidget.Text() self._stack.addWidget(self.txt) @@ -147,8 +148,8 @@ class StatusBar(QWidget): self._stack.addWidget(self.prompt) self._previous_widget = PreviousWidget.none - self.cmd.show_cmd.connect(self._show_cmd_widget) - self.cmd.hide_cmd.connect(self._hide_cmd_widget) + self._cmd.show_cmd.connect(self._show_cmd_widget) + self._cmd.hide_cmd.connect(self._hide_cmd_widget) self._hide_cmd_widget() self.prompt.show_prompt.connect(self._show_prompt_widget) self.prompt.hide_prompt.connect(self._hide_prompt_widget) @@ -260,7 +261,7 @@ class StatusBar(QWidget): if self._text_pop_timer.isActive(): self._timer_was_active = True self._text_pop_timer.stop() - self._stack.setCurrentWidget(self.cmd) + self._stack.setCurrentWidget(self._cmd) def _hide_cmd_widget(self): """Show temporary text instead of command widget.""" diff --git a/qutebrowser/widgets/statusbar/command.py b/qutebrowser/widgets/statusbar/command.py index 3ff65ee64..97dd95b22 100644 --- a/qutebrowser/widgets/statusbar/command.py +++ b/qutebrowser/widgets/statusbar/command.py @@ -160,7 +160,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): self.setFocus() self.show_cmd.emit() - @cmdutils.register(instance='mainwindow.status.cmd', name='set-cmd-text') + @cmdutils.register(instance='status-cmd', name='set-cmd-text') def set_cmd_text_command(self, text): """Preset the statusbar to some text. @@ -216,7 +216,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): self.setFocus() self.show_cmd.emit() - @cmdutils.register(instance='mainwindow.status.cmd', hide=True, + @cmdutils.register(instance='status-cmd', hide=True, modes=[usertypes.KeyMode.command]) def command_history_prev(self): """Go back in the commandline history.""" @@ -231,7 +231,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): if item: self.set_cmd_text(item) - @cmdutils.register(instance='mainwindow.status.cmd', hide=True, + @cmdutils.register(instance='status-cmd', hide=True, modes=[usertypes.KeyMode.command]) def command_history_next(self): """Go forward in the commandline history.""" @@ -244,7 +244,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): if item: self.set_cmd_text(item) - @cmdutils.register(instance='mainwindow.status.cmd', hide=True, + @cmdutils.register(instance='status-cmd', hide=True, modes=[usertypes.KeyMode.command]) def command_accept(self): """Execute the command currently in the commandline. From ffaf4f0cb085a49959f4302097e5601941680e26 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 21:49:22 +0200 Subject: [PATCH 25/79] Use object registry for completion. --- qutebrowser/app.py | 2 +- qutebrowser/widgets/completion.py | 4 ++-- qutebrowser/widgets/mainwindow.py | 12 +++++++----- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index c9b521f50..1cfdbc876 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -372,7 +372,7 @@ class Application(QApplication): # syntactic sugar kp = self._keyparsers status = self.mainwindow.status - completion = self.mainwindow.completion + completion = self.registry['completion'] tabs = self.mainwindow.tabs cmd = self.registry['status-cmd'] completer = self.registry['completer'] diff --git a/qutebrowser/widgets/completion.py b/qutebrowser/widgets/completion.py index 12e2dc6b5..68da7576a 100644 --- a/qutebrowser/widgets/completion.py +++ b/qutebrowser/widgets/completion.py @@ -210,13 +210,13 @@ class CompletionView(QTreeView): selmod.clearSelection() selmod.clearCurrentIndex() - @cmdutils.register(instance='mainwindow.completion', hide=True, + @cmdutils.register(instance='completion', hide=True, modes=[usertypes.KeyMode.command]) def completion_item_prev(self): """Select the previous completion item.""" self._next_prev_item(prev=True) - @cmdutils.register(instance='mainwindow.completion', hide=True, + @cmdutils.register(instance='completion', hide=True, modes=[usertypes.KeyMode.command]) def completion_item_next(self): """Select the next completion item.""" diff --git a/qutebrowser/widgets/mainwindow.py b/qutebrowser/widgets/mainwindow.py index cf41e21fc..ab7bb7917 100644 --- a/qutebrowser/widgets/mainwindow.py +++ b/qutebrowser/widgets/mainwindow.py @@ -85,7 +85,8 @@ class MainWindow(QWidget): self.tabs.title_changed.connect(self.setWindowTitle) self._vbox.addWidget(self.tabs) - self.completion = completion.CompletionView(self) + self._completion = completion.CompletionView(self) + utils.register_object('completion', self._completion) self.status = bar.StatusBar() self._vbox.addWidget(self.status) @@ -93,7 +94,8 @@ class MainWindow(QWidget): # When we're here the statusbar might not even really exist yet, so # resizing will fail. Therefore, we use singleShot QTimers to make sure # we defer this until everything else is initialized. - QTimer.singleShot(0, lambda: self.completion.resize_completion.connect( + QTimer.singleShot( + 0, lambda: self._completion.resize_completion.connect( self.resize_completion)) QTimer.singleShot(0, self.resize_completion) #self.retranslateUi(MainWindow) @@ -126,8 +128,8 @@ class MainWindow(QWidget): # Shrink to content size if needed and shrinking is enabled if config.get('completion', 'shrink'): contents_height = ( - self.completion.viewportSizeHint().height() + - self.completion.horizontalScrollBar().sizeHint().height()) + self._completion.viewportSizeHint().height() + + self._completion.horizontalScrollBar().sizeHint().height()) if contents_height <= height: height = contents_height else: @@ -140,7 +142,7 @@ class MainWindow(QWidget): bottomright = self.status.geometry().topRight() rect = QRect(topleft, bottomright) if rect.isValid(): - self.completion.setGeometry(rect) + self._completion.setGeometry(rect) @cmdutils.register(instance='mainwindow', name=['quit', 'q']) def close(self): From 487300f926ee97ad27293fdcc172daf1a61d4f07 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 21:54:11 +0200 Subject: [PATCH 26/79] Use object registry for prompter. --- qutebrowser/app.py | 7 ++++--- qutebrowser/widgets/statusbar/prompt.py | 5 +++-- qutebrowser/widgets/statusbar/prompter.py | 8 ++++---- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 1cfdbc876..2b0509491 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -379,6 +379,7 @@ class Application(QApplication): searchrunner = self.registry['searchrunner'] messagebridge = self.registry['messagebridge'] modeman = self.registry['modeman'] + prompter = self.registry['prompter'] # misc self.lastWindowClosed.connect(self.shutdown) @@ -388,7 +389,7 @@ class Application(QApplication): modeman.entered.connect(status.on_mode_entered) modeman.left.connect(status.on_mode_left) modeman.left.connect(cmd.on_mode_left) - modeman.left.connect(status.prompt.prompter.on_mode_left) + modeman.left.connect(prompter.on_mode_left) # commands cmd.got_cmd.connect(self._commandrunner.run_safely) @@ -413,7 +414,7 @@ class Application(QApplication): messagebridge.s_set_text.connect(status.set_text) messagebridge.s_maybe_reset_text.connect(status.txt.maybe_reset_text) messagebridge.s_set_cmd_text.connect(cmd.set_cmd_text) - messagebridge.s_question.connect(status.prompt.prompter.ask_question, + messagebridge.s_question.connect(prompter.ask_question, Qt.DirectConnection) # config @@ -730,7 +731,7 @@ class Application(QApplication): return self._shutting_down = True log.destroy.debug("Shutting down with status {}...".format(status)) - if self.mainwindow.status.prompt.prompter.shutdown(): + if self.registry['prompter'].shutdown(): # If shutdown was called while we were asking a question, we're in # a still sub-eventloop (which gets quitted now) and not in the # main one. diff --git a/qutebrowser/widgets/statusbar/prompt.py b/qutebrowser/widgets/statusbar/prompt.py index 80ed8405a..3122703dd 100644 --- a/qutebrowser/widgets/statusbar/prompt.py +++ b/qutebrowser/widgets/statusbar/prompt.py @@ -24,6 +24,7 @@ from PyQt5.QtWidgets import QHBoxLayout, QWidget, QLineEdit from qutebrowser.widgets import misc from qutebrowser.widgets.statusbar import textbase, prompter +from qutebrowser.utils import utils class PromptLineEdit(misc.MinimalLineEditMixin, QLineEdit): @@ -40,7 +41,6 @@ class Prompt(QWidget): """The prompt widget shown in the statusbar. Attributes: - prompter: The Prompter instance assosciated with this Prompt. txt: The TextBase instance (QLabel) used to display the prompt text. lineedit: The MinimalLineEdit instance (QLineEdit) used for the input. _hbox: The QHBoxLayout used to display the text and prompt. @@ -65,7 +65,8 @@ class Prompt(QWidget): self.lineedit = PromptLineEdit() self._hbox.addWidget(self.lineedit) - self.prompter = prompter.Prompter(self) + prompter_obj = prompter.Prompter(self) + utils.register_object('prompter', prompter_obj) def __repr__(self): return '<{}>'.format(self.__class__.__name__) diff --git a/qutebrowser/widgets/statusbar/prompter.py b/qutebrowser/widgets/statusbar/prompter.py index ec62551ad..9cfbb7cb1 100644 --- a/qutebrowser/widgets/statusbar/prompter.py +++ b/qutebrowser/widgets/statusbar/prompter.py @@ -26,7 +26,7 @@ from PyQt5.QtWidgets import QLineEdit from qutebrowser.keyinput import modeman from qutebrowser.commands import cmdutils -from qutebrowser.utils import usertypes, log, qtutils +from qutebrowser.utils import usertypes, log, qtutils, utils PromptContext = collections.namedtuple('PromptContext', @@ -185,7 +185,7 @@ class Prompter: if self.question.answer is None and not self.question.is_aborted: self.question.cancel() - @cmdutils.register(instance='mainwindow.status.prompt.prompter', hide=True, + @cmdutils.register(instance='prompter', hide=True, modes=[usertypes.KeyMode.prompt, usertypes.KeyMode.yesno]) def prompt_accept(self): @@ -227,7 +227,7 @@ class Prompter: else: raise ValueError("Invalid question mode!") - @cmdutils.register(instance='mainwindow.status.prompt.prompter', hide=True, + @cmdutils.register(instance='prompter', hide=True, modes=[usertypes.KeyMode.yesno]) def prompt_yes(self): """Answer yes to a yes/no prompt.""" @@ -238,7 +238,7 @@ class Prompter: modeman.leave(usertypes.KeyMode.yesno, 'yesno accept') self.question.done() - @cmdutils.register(instance='mainwindow.status.prompt.prompter', hide=True, + @cmdutils.register(instance='prompter', hide=True, modes=[usertypes.KeyMode.yesno]) def prompt_no(self): """Answer no to a yes/no prompt.""" From a76c4c8ba58b279336884dc00d31f70b916cabb8 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 22:00:26 +0200 Subject: [PATCH 27/79] Use object registry for tabbedbrowser. --- qutebrowser/app.py | 31 +++++++++++++----------- qutebrowser/commands/argparser.py | 4 +-- qutebrowser/commands/runners.py | 5 ++-- qutebrowser/widgets/mainwindow.py | 13 +++++----- qutebrowser/widgets/statusbar/command.py | 7 +++--- 5 files changed, 31 insertions(+), 29 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 2b0509491..bab2438f5 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -327,7 +327,7 @@ class Application(QApplication): # we make sure the GUI is refreshed here, so the start seems faster. self.processEvents(QEventLoop.ExcludeUserInputEvents | QEventLoop.ExcludeSocketNotifiers) - + tabbedbrowser = self.registry['tabbedbrowser'] for cmd in self.args.command: if cmd.startswith(':'): log.init.debug("Startup cmd {}".format(cmd)) @@ -340,9 +340,9 @@ class Application(QApplication): message.error("Error in startup argument '{}': {}".format( cmd, e)) else: - self.mainwindow.tabs.tabopen(url) + tabbedbrowser.tabopen(url) - if self.mainwindow.tabs.count() == 0: + if tabbedbrowser.count() == 0: log.init.debug("Opening startpage") for urlstr in self.config.get('general', 'startpage'): try: @@ -350,7 +350,7 @@ class Application(QApplication): except urlutils.FuzzyUrlError as e: message.error("Error when opening startpage: {}".format(e)) else: - self.mainwindow.tabs.tabopen(url) + tabbedbrowser.tabopen(url) def _python_hacks(self): """Get around some PyQt-oddities by evil hacks. @@ -373,7 +373,7 @@ class Application(QApplication): kp = self._keyparsers status = self.mainwindow.status completion = self.registry['completion'] - tabs = self.mainwindow.tabs + tabs = self.registry['tabbedbrowser'] cmd = self.registry['status-cmd'] completer = self.registry['completer'] searchrunner = self.registry['searchrunner'] @@ -509,12 +509,12 @@ class Application(QApplication): Return: A list of open pages, or an empty list. """ + try: + tabbedbrowser = self.registry['tabbedbrowser'] + except KeyError: + return [] pages = [] - if self.mainwindow is None: - return pages - if self.mainwindow.tabs is None: - return pages - for tab in self.mainwindow.tabs.widgets(): + for tab in tabbedbrowser.widgets(): try: url = tab.cur_url.toString( QUrl.RemovePassword | QUrl.FullyEncoded) @@ -617,7 +617,7 @@ class Application(QApplication): # exceptions occur. if pages is None: pages = [] - for tab in self.mainwindow.tabs.widgets(): + for tab in utils.get_object('tabbedbrowser').widgets(): urlstr = tab.cur_url.toString( QUrl.RemovePassword | QUrl.FullyEncoded) if urlstr: @@ -664,7 +664,8 @@ class Application(QApplication): except Exception: # pylint: disable=broad-except out = traceback.format_exc() qutescheme.pyeval_output = out - self.mainwindow.tabs.openurl(QUrl('qute:pyeval'), newtab=True) + self.registry['tabbedbrowser'].openurl( + QUrl('qute:pyeval'), newtab=True) @cmdutils.register(instance='') def report(self): @@ -755,9 +756,11 @@ class Application(QApplication): except KeyError: pass # Close all tabs - if self.mainwindow is not None: + try: log.destroy.debug("Closing tabs...") - self.mainwindow.tabs.shutdown() + self.registry['tabbedbrowser'].shutdown() + except KeyError: + pass # Save everything if hasattr(self, 'config') and self.config is not None: to_save = [] diff --git a/qutebrowser/commands/argparser.py b/qutebrowser/commands/argparser.py index 884582a4b..3919561cb 100644 --- a/qutebrowser/commands/argparser.py +++ b/qutebrowser/commands/argparser.py @@ -22,7 +22,7 @@ import argparse -from PyQt5.QtCore import QCoreApplication, QUrl +from PyQt5.QtCore import QUrl from qutebrowser.commands import cmdexc from qutebrowser.utils import utils @@ -54,7 +54,7 @@ class HelpAction(argparse.Action): """ def __call__(self, parser, _namespace, _values, _option_string=None): - QCoreApplication.instance().mainwindow.tabs.tabopen( + utils.get_object('tabbedbrowser').tabopen( QUrl('qute://help/commands.html#{}'.format(parser.name))) parser.exit() diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py index 02ac4477e..6d710a59c 100644 --- a/qutebrowser/commands/runners.py +++ b/qutebrowser/commands/runners.py @@ -19,7 +19,7 @@ """Module containing command managers (SearchRunner and CommandRunner).""" -from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QCoreApplication, QUrl +from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject, QUrl from PyQt5.QtWebKitWidgets import QWebPage from qutebrowser.config import config @@ -32,8 +32,7 @@ def replace_variables(arglist): args = [] for arg in arglist: if arg == '{url}': - app = QCoreApplication.instance() - url = app.mainwindow.tabs.current_url().toString( + url = utils.get_object('tabbedbrowser').current_url().toString( QUrl.FullyEncoded | QUrl.RemovePassword) args.append(url) else: diff --git a/qutebrowser/widgets/mainwindow.py b/qutebrowser/widgets/mainwindow.py index ab7bb7917..a24a26804 100644 --- a/qutebrowser/widgets/mainwindow.py +++ b/qutebrowser/widgets/mainwindow.py @@ -40,9 +40,9 @@ class MainWindow(QWidget): signals. Attributes: - tabs: The TabbedBrowser widget. status: The StatusBar widget. downloadview: The DownloadView widget. + _tabbedbrowser: The TabbedBrowser widget. _vbox: The main QVBoxLayout. """ @@ -81,9 +81,10 @@ class MainWindow(QWidget): self._vbox.addWidget(self.downloadview) self.downloadview.show() - self.tabs = tabbedbrowser.TabbedBrowser() - self.tabs.title_changed.connect(self.setWindowTitle) - self._vbox.addWidget(self.tabs) + self._tabbedbrowser = tabbedbrowser.TabbedBrowser() + self._tabbedbrowser.title_changed.connect(self.setWindowTitle) + utils.register_object('tabbedbrowser', self._tabbedbrowser) + self._vbox.addWidget(self._tabbedbrowser) self._completion = completion.CompletionView(self) utils.register_object('completion', self._completion) @@ -163,12 +164,12 @@ class MainWindow(QWidget): super().resizeEvent(e) self.resize_completion() self.downloadview.updateGeometry() - self.tabs.tabBar().refresh() + self._tabbedbrowser.tabBar().refresh() def closeEvent(self, e): """Override closeEvent to display a confirmation if needed.""" confirm_quit = config.get('ui', 'confirm-quit') - count = self.tabs.count() + count = self._tabbedbrowser.count() if confirm_quit == 'never': e.accept() elif confirm_quit == 'multiple-tabs' and count <= 1: diff --git a/qutebrowser/widgets/statusbar/command.py b/qutebrowser/widgets/statusbar/command.py index 97dd95b22..e199d5a22 100644 --- a/qutebrowser/widgets/statusbar/command.py +++ b/qutebrowser/widgets/statusbar/command.py @@ -19,14 +19,14 @@ """The commandline in the statusbar.""" -from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QCoreApplication, QUrl +from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QUrl from PyQt5.QtWidgets import QSizePolicy, QApplication from qutebrowser.keyinput import modeman, modeparsers from qutebrowser.commands import runners, cmdexc, cmdutils from qutebrowser.widgets import misc from qutebrowser.models import cmdhistory -from qutebrowser.utils import usertypes, log +from qutebrowser.utils import usertypes, log, utils class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): @@ -172,8 +172,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): Args: text: The commandline to set. """ - app = QCoreApplication.instance() - url = app.mainwindow.tabs.current_url().toString( + url = utils.get_object('tabbedbrowser').current_url().toString( QUrl.FullyEncoded | QUrl.RemovePassword) # FIXME we currently replace the URL in any place in the arguments, # rather than just replacing it if it is a dedicated argument. We could From a32ed36ba6cd4a1b6f996e8fce18260be8248539 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 22:02:21 +0200 Subject: [PATCH 28/79] Use object registry for mainwindow. --- qutebrowser/app.py | 13 +++++++------ qutebrowser/keyinput/modeman.py | 5 ++--- qutebrowser/widgets/tabwidget.py | 7 +++---- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index bab2438f5..1a833dff5 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -135,8 +135,8 @@ class Application(QApplication): log.init.debug("Initializing downloads...") self.downloadmanager = downloads.DownloadManager(self) log.init.debug("Initializing main window...") - self.mainwindow = mainwindow.MainWindow() - modeman_obj.mainwindow = self.mainwindow + mainwin = mainwindow.MainWindow() + self.registry['mainwindow'] = mainwin log.init.debug("Initializing debug console...") self._debugconsole = console.ConsoleWidget() log.init.debug("Initializing eventfilter...") @@ -149,7 +149,7 @@ class Application(QApplication): log.init.debug("Showing mainwindow...") if not args.nowindow: - self.mainwindow.show() + mainwin.show() log.init.debug("Applying python hacks...") self._python_hacks() QTimer.singleShot(0, self._process_init_args) @@ -371,7 +371,8 @@ class Application(QApplication): # pylint: disable=too-many-statements # syntactic sugar kp = self._keyparsers - status = self.mainwindow.status + mainwin = self.registry['mainwindow'] + status = mainwin.status completion = self.registry['completion'] tabs = self.registry['tabbedbrowser'] cmd = self.registry['status-cmd'] @@ -419,7 +420,7 @@ class Application(QApplication): # config self.config.style_changed.connect(style.get_stylesheet.cache_clear) - for obj in (tabs, completion, self.mainwindow, self.cmd_history, + for obj in (tabs, completion, mainwin, self.cmd_history, websettings, modeman, status, status.txt): self.config.changed.connect(obj.on_config_changed) for obj in kp.values(): @@ -527,7 +528,7 @@ class Application(QApplication): def _save_geometry(self): """Save the window geometry to the state config.""" stateconfig = self.registry['stateconfig'] - data = bytes(self.mainwindow.saveGeometry()) + data = bytes(self.registry['mainwindow'].saveGeometry()) geom = base64.b64encode(data).decode('ASCII') try: stateconfig.add_section('geometry') diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index 394c07388..3912717da 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -70,7 +70,6 @@ class ModeManager(QObject): Attributes: passthrough: A list of modes in which to pass through events. - mainwindow: The mainwindow object locked: Whether current mode is locked. This means the current mode can still be left (then locked will be reset), but no new mode can be entered while the current mode is active. @@ -94,7 +93,6 @@ class ModeManager(QObject): def __init__(self, parent=None): super().__init__(parent) - self.mainwindow = None self.locked = False self._handlers = {} self.passthrough = [] @@ -284,7 +282,8 @@ class ModeManager(QObject): # We already handled this same event at some point earlier, so # we're not interested in it anymore. return False - if QApplication.instance().activeWindow() is not self.mainwindow: + if (QApplication.instance().activeWindow() is not + utils.get_object('mainwindow')): # Some other window (print dialog, etc.) is focused so we pass # the event through. return False diff --git a/qutebrowser/widgets/tabwidget.py b/qutebrowser/widgets/tabwidget.py index 26e8b2df7..68a9e9627 100644 --- a/qutebrowser/widgets/tabwidget.py +++ b/qutebrowser/widgets/tabwidget.py @@ -28,11 +28,10 @@ import functools from PyQt5.QtCore import pyqtSlot, Qt, QSize, QRect, QPoint, QTimer from PyQt5.QtWidgets import (QTabWidget, QTabBar, QSizePolicy, QCommonStyle, - QStyle, QStylePainter, QStyleOptionTab, - QApplication) + QStyle, QStylePainter, QStyleOptionTab) from PyQt5.QtGui import QIcon, QPalette, QColor -from qutebrowser.utils import qtutils +from qutebrowser.utils import qtutils, utils from qutebrowser.config import config @@ -210,7 +209,7 @@ class TabBar(QTabBar): confwidth = str(config.get('tabs', 'width')) if confwidth.endswith('%'): perc = int(confwidth.rstrip('%')) - width = QApplication.instance().mainwindow.width() * perc / 100 + width = utils.get_object('mainwindow').width() * perc / 100 else: width = int(confwidth) size = QSize(max(minimum_size.width(), width), height) From 37dbfde6ac877baa107e1d493f14f3f9323a173b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 22:06:46 +0200 Subject: [PATCH 29/79] Use object registry for app. --- qutebrowser/app.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 1a833dff5..3ab4bdec9 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -90,6 +90,7 @@ class Application(QApplication): self.meta_registry = utypes.ObjectRegistry() self.registry = utypes.ObjectRegistry() self.meta_registry['global'] = self.registry + self.registry['app'] = self self._shutting_down = False self._keyparsers = None self._crashdlg = None @@ -611,7 +612,7 @@ class Application(QApplication): self._destroy_crashlogfile() sys.exit(1) - @cmdutils.register(instance='', ignore_args=True) + @cmdutils.register(instance='app', ignore_args=True) def restart(self, shutdown=True, pages=None): """Restart qutebrowser while keeping existing tabs open.""" # We don't use _recover_pages here as it's too forgiving when @@ -647,7 +648,7 @@ class Application(QApplication): if shutdown: self.shutdown() - @cmdutils.register(instance='', split=False, debug=True) + @cmdutils.register(instance='app', split=False, debug=True) def debug_pyeval(self, s): """Evaluate a python string and display the results as a webpage. @@ -668,7 +669,7 @@ class Application(QApplication): self.registry['tabbedbrowser'].openurl( QUrl('qute:pyeval'), newtab=True) - @cmdutils.register(instance='') + @cmdutils.register(instance='app') def report(self): """Report a bug in qutebrowser.""" pages = self._recover_pages() @@ -677,7 +678,7 @@ class Application(QApplication): self._crashdlg = crash.ReportDialog(pages, history, objects) self._crashdlg.show() - @cmdutils.register(instance='', debug=True, name='debug-console') + @cmdutils.register(instance='app', debug=True, name='debug-console') def show_debugconsole(self): """Show the debugging console.""" self._debugconsole.show() From 297e37fdf2e7e61638e879779632b52e68183b54 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 22:08:05 +0200 Subject: [PATCH 30/79] Remove legacy instance= parameter support. --- qutebrowser/commands/command.py | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/qutebrowser/commands/command.py b/qutebrowser/commands/command.py index 4a13e97bd..825a5b9f0 100644 --- a/qutebrowser/commands/command.py +++ b/qutebrowser/commands/command.py @@ -22,7 +22,6 @@ import inspect import collections -from PyQt5.QtCore import QCoreApplication from PyQt5.QtWebKit import QWebSettings from qutebrowser.commands import cmdexc, argparser @@ -299,15 +298,7 @@ class Command: args: The positional argument list. Gets modified directly. """ assert param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD - app = QCoreApplication.instance() - if self.instance == '': - obj = app - elif self.instance in app.registry: - # Use object registry where available - obj = app.registry[self.instance] - else: - # FIXME remove this - obj = utils.dotted_getattr(app, self.instance) + obj = utils.get_object(self.instance) args.append(obj) def _get_count_arg(self, param, args, kwargs): From 8af8e3530fae55f512d27c3a498d36ef28a5d66b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 22:13:10 +0200 Subject: [PATCH 31/79] Use object registry for args. --- qutebrowser/app.py | 17 +++++++++-------- qutebrowser/models/completion.py | 10 ++++------ 2 files changed, 13 insertions(+), 14 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 3ab4bdec9..0ad1f3dae 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -58,7 +58,7 @@ class Application(QApplication): mainwindow: The MainWindow QWidget. config: The main ConfigManager cmd_history: The "cmd_history" LineConfigParser instance. - args: ArgumentParser instance. + _args: ArgumentParser instance. _commandrunner: The main CommandRunner instance. _debugconsole: The ConsoleWidget for debugging. _keyparsers: A mapping from modes to keyparsers. @@ -101,7 +101,8 @@ class Application(QApplication): sys.excepthook = self._exception_hook - self.args = args + self._args = args + self.registry['args'] = args log.init.debug("Starting init...") self._init_misc() utils.actute_warning() @@ -162,12 +163,12 @@ class Application(QApplication): def _init_config(self): """Inizialize and read the config.""" - if self.args.confdir is None: + if self._args.confdir is None: confdir = utils.get_standard_dir(QStandardPaths.ConfigLocation) - elif self.args.confdir == '': + elif self._args.confdir == '': confdir = None else: - confdir = self.args.confdir + confdir = self._args.confdir try: self.config = config.ConfigManager(confdir, 'qutebrowser.conf', self) @@ -251,7 +252,7 @@ class Application(QApplication): def _init_misc(self): """Initialize misc things.""" - if self.args.version: + if self._args.version: print(version.version()) print() print() @@ -329,7 +330,7 @@ class Application(QApplication): self.processEvents(QEventLoop.ExcludeUserInputEvents | QEventLoop.ExcludeSocketNotifiers) tabbedbrowser = self.registry['tabbedbrowser'] - for cmd in self.args.command: + for cmd in self._args.command: if cmd.startswith(':'): log.init.debug("Startup cmd {}".format(cmd)) self._commandrunner.run_safely_init(cmd.lstrip(':')) @@ -813,7 +814,7 @@ class Application(QApplication): def exit(self, status): """Extend QApplication::exit to log the event.""" log.destroy.debug("Now calling QApplication::exit.") - if self.args.debug_exit: + if self._args.debug_exit: print("Now logging late shutdown.", file=sys.stderr) debug.trace_lines(True) super().exit(status) diff --git a/qutebrowser/models/completion.py b/qutebrowser/models/completion.py index e09622554..3b2c66dc1 100644 --- a/qutebrowser/models/completion.py +++ b/qutebrowser/models/completion.py @@ -19,11 +19,11 @@ """CompletionModels for different usages.""" -from PyQt5.QtCore import pyqtSlot, Qt, QCoreApplication +from PyQt5.QtCore import pyqtSlot, Qt from qutebrowser.config import config, configdata from qutebrowser.models import basecompletion -from qutebrowser.utils import log, qtutils +from qutebrowser.utils import log, qtutils, utils from qutebrowser.commands import cmdutils @@ -155,8 +155,7 @@ class CommandCompletionModel(basecompletion.BaseCompletionModel): assert cmdutils.cmd_dict cmdlist = [] for obj in set(cmdutils.cmd_dict.values()): - if obj.hide or (obj.debug and not - QCoreApplication.instance().args.debug): + if obj.hide or (obj.debug and not utils.get_object('args').debug): pass else: cmdlist.append((obj.name, obj.desc)) @@ -183,8 +182,7 @@ class HelpCompletionModel(basecompletion.BaseCompletionModel): assert cmdutils.cmd_dict cmdlist = [] for obj in set(cmdutils.cmd_dict.values()): - if obj.hide or (obj.debug and not - QCoreApplication.instance().args.debug): + if obj.hide or (obj.debug and not utils.get_object('args').debug): pass else: cmdlist.append((':' + obj.name, obj.desc)) From 7a7b397c153f0f8e0b61dda34df1f54269561c18 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 22:17:25 +0200 Subject: [PATCH 32/79] Use object registry for cmd-history. --- qutebrowser/app.py | 16 ++++++++++------ qutebrowser/widgets/statusbar/command.py | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 0ad1f3dae..79a1a03c6 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -57,7 +57,6 @@ class Application(QApplication): meta_registry: The object registry of object registries. mainwindow: The MainWindow QWidget. config: The main ConfigManager - cmd_history: The "cmd_history" LineConfigParser instance. _args: ArgumentParser instance. _commandrunner: The main CommandRunner instance. _debugconsole: The ConsoleWidget for debugging. @@ -95,7 +94,6 @@ class Application(QApplication): self._keyparsers = None self._crashdlg = None self._crashlogfile = None - self.cmd_history = None self.config = None self.keyconfig = None @@ -207,8 +205,9 @@ class Application(QApplication): sys.exit(1) stateconfig = iniparsers.ReadWriteConfigParser(confdir, 'state') self.registry['stateconfig'] = stateconfig - self.cmd_history = lineparser.LineConfigParser( + cmd_history = lineparser.LineConfigParser( confdir, 'cmd_history', ('completion', 'history-length')) + self.registry['cmd_history'] = cmd_history def _init_modes(self): """Inizialize the mode manager and the keyparsers.""" @@ -383,6 +382,7 @@ class Application(QApplication): messagebridge = self.registry['messagebridge'] modeman = self.registry['modeman'] prompter = self.registry['prompter'] + cmd_history = self.registry['cmd_history'] # misc self.lastWindowClosed.connect(self.shutdown) @@ -422,7 +422,7 @@ class Application(QApplication): # config self.config.style_changed.connect(style.get_stylesheet.cache_clear) - for obj in (tabs, completion, mainwin, self.cmd_history, + for obj in (tabs, completion, mainwin, cmd_history, websettings, modeman, status, status.txt): self.config.changed.connect(obj.on_config_changed) for obj in kp.values(): @@ -774,8 +774,12 @@ class Application(QApplication): to_save.append(("keyconfig", self.keyconfig.save)) to_save += [("window geometry", self._save_geometry), ("quickmarks", quickmarks.save)] - if hasattr(self, 'cmd_history'): - to_save.append(("command history", self.cmd_history.save)) + try: + cmd_history = self.registry['cmd_history'] + except KeyError: + pass + else: + to_save.append(("command history", cmd_history.save)) try: stateconfig = self.registry['stateconfig'] except KeyError: diff --git a/qutebrowser/widgets/statusbar/command.py b/qutebrowser/widgets/statusbar/command.py index e199d5a22..cc6d75525 100644 --- a/qutebrowser/widgets/statusbar/command.py +++ b/qutebrowser/widgets/statusbar/command.py @@ -20,7 +20,7 @@ """The commandline in the statusbar.""" from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QUrl -from PyQt5.QtWidgets import QSizePolicy, QApplication +from PyQt5.QtWidgets import QSizePolicy from qutebrowser.keyinput import modeman, modeparsers from qutebrowser.commands import runners, cmdexc, cmdutils @@ -74,7 +74,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): misc.CommandLineEdit.__init__(self, parent) misc.MinimalLineEditMixin.__init__(self) self.cursor_part = 0 - self.history.history = QApplication.instance().cmd_history.data + self.history.history = utils.get_object('cmd_history').data self._empty_item_idx = None self.textEdited.connect(self.on_text_edited) self.cursorPositionChanged.connect(self._update_cursor_part) From b818bc589633cf6b837d9c9674cb71f2e0f97ea4 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 22:22:03 +0200 Subject: [PATCH 33/79] Use object registry for downloadmanager. --- qutebrowser/app.py | 8 +++++--- qutebrowser/models/downloadmodel.py | 19 +++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 79a1a03c6..719dd71cf 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -133,7 +133,8 @@ class Application(QApplication): searchrunner = runners.SearchRunner(self) self.registry['searchrunner'] = searchrunner log.init.debug("Initializing downloads...") - self.downloadmanager = downloads.DownloadManager(self) + downloadmanager = downloads.DownloadManager(self) + self.registry['downloadmanager'] = downloadmanager log.init.debug("Initializing main window...") mainwin = mainwindow.MainWindow() self.registry['mainwindow'] = mainwin @@ -383,6 +384,7 @@ class Application(QApplication): modeman = self.registry['modeman'] prompter = self.registry['prompter'] cmd_history = self.registry['cmd_history'] + downloadmanager = self.registry['downloadmanager'] # misc self.lastWindowClosed.connect(self.shutdown) @@ -457,8 +459,8 @@ class Application(QApplication): completer.change_completed_part.connect(cmd.on_change_completed_part) # downloads - tabs.start_download.connect(self.downloadmanager.fetch) - tabs.download_get.connect(self.downloadmanager.get) + tabs.start_download.connect(downloadmanager.fetch) + tabs.download_get.connect(downloadmanager.get) def _get_widgets(self): """Get a string list of all widgets.""" diff --git a/qutebrowser/models/downloadmodel.py b/qutebrowser/models/downloadmodel.py index 36aa4f89c..f2495d2ff 100644 --- a/qutebrowser/models/downloadmodel.py +++ b/qutebrowser/models/downloadmodel.py @@ -21,10 +21,9 @@ from PyQt5.QtCore import (pyqtSlot, Qt, QVariant, QAbstractListModel, QModelIndex) -from PyQt5.QtWidgets import QApplication from qutebrowser.config import config -from qutebrowser.utils import usertypes, qtutils +from qutebrowser.utils import usertypes, qtutils, utils Role = usertypes.enum('Role', 'item', start=Qt.UserRole, is_int=True) @@ -40,14 +39,14 @@ class DownloadModel(QAbstractListModel): def __init__(self, parent=None): super().__init__(parent) - self.downloadmanager = QApplication.instance().downloadmanager - self.downloadmanager.download_about_to_be_added.connect( + downloadmanager = utils.get_object('downloadmanager') + downloadmanager.download_about_to_be_added.connect( lambda idx: self.beginInsertRows(QModelIndex(), idx, idx)) - self.downloadmanager.download_added.connect(self.endInsertRows) - self.downloadmanager.download_about_to_be_finished.connect( + downloadmanager.download_added.connect(self.endInsertRows) + downloadmanager.download_about_to_be_finished.connect( lambda idx: self.beginRemoveRows(QModelIndex(), idx, idx)) - self.downloadmanager.download_finished.connect(self.endRemoveRows) - self.downloadmanager.data_changed.connect(self.on_data_changed) + downloadmanager.download_finished.connect(self.endRemoveRows) + downloadmanager.data_changed.connect(self.on_data_changed) def __repr__(self): return '<{}>'.format(self.__class__.__name__) @@ -82,7 +81,7 @@ class DownloadModel(QAbstractListModel): if index.parent().isValid() or index.column() != 0: return QVariant() - item = self.downloadmanager.downloads[index.row()] + item = utils.get_object('downloadmanager').downloads[index.row()] if role == Qt.DisplayRole: data = str(item) elif role == Qt.ForegroundRole: @@ -106,4 +105,4 @@ class DownloadModel(QAbstractListModel): if parent.isValid(): # We don't have children return 0 - return len(self.downloadmanager.downloads) + return len(utils.get_object('downloadmanager').downloads) From 07dde5be808502cdc74864154a9d8205998863d8 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 22:22:44 +0200 Subject: [PATCH 34/79] Fix downloading (getting messagebrige object). --- qutebrowser/browser/downloads.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py index b481956a5..99ccf674e 100644 --- a/qutebrowser/browser/downloads.py +++ b/qutebrowser/browser/downloads.py @@ -409,7 +409,7 @@ class DownloadManager(QObject): q.destroyed.connect(functools.partial(self.questions.remove, q)) self.questions.append(q) download.cancelled.connect(q.abort) - utils.get_object('message').ask(q, blocking=False) + utils.get_object('messagebridge').ask(q, blocking=False) @pyqtSlot(DownloadItem) def on_finished(self, download): From 953e50721ccd3933e6b9e4e7e0e63b48f0da9bfe Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 22:28:28 +0200 Subject: [PATCH 35/79] Use object registry for config. --- qutebrowser/app.py | 29 ++++++++++++++++------------- qutebrowser/config/config.py | 13 ++++--------- qutebrowser/config/style.py | 6 +++--- qutebrowser/utils/completer.py | 7 ++++--- 4 files changed, 27 insertions(+), 28 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 719dd71cf..0abf62d48 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -56,7 +56,6 @@ class Application(QApplication): registry: The object registry of global objects. meta_registry: The object registry of object registries. mainwindow: The MainWindow QWidget. - config: The main ConfigManager _args: ArgumentParser instance. _commandrunner: The main CommandRunner instance. _debugconsole: The ConsoleWidget for debugging. @@ -94,7 +93,6 @@ class Application(QApplication): self._keyparsers = None self._crashdlg = None self._crashlogfile = None - self.config = None self.keyconfig = None sys.excepthook = self._exception_hook @@ -169,8 +167,8 @@ class Application(QApplication): else: confdir = self._args.confdir try: - self.config = config.ConfigManager(confdir, 'qutebrowser.conf', - self) + config_obj = config.ConfigManager( + confdir, 'qutebrowser.conf', self) except (configtypes.ValidationError, config.NoOptionError, config.NoSectionError, @@ -190,6 +188,8 @@ class Application(QApplication): msgbox.exec_() # We didn't really initialize much so far, so we just quit hard. sys.exit(1) + else: + self.registry['config'] = config_obj try: self.keyconfig = keyconfparser.KeyConfigParser( confdir, 'keys.conf') @@ -346,7 +346,7 @@ class Application(QApplication): if tabbedbrowser.count() == 0: log.init.debug("Opening startpage") - for urlstr in self.config.get('general', 'startpage'): + for urlstr in config.get('general', 'startpage'): try: url = urlutils.fuzzy_url(urlstr) except urlutils.FuzzyUrlError as e: @@ -385,6 +385,7 @@ class Application(QApplication): prompter = self.registry['prompter'] cmd_history = self.registry['cmd_history'] downloadmanager = self.registry['downloadmanager'] + config_obj = self.registry['config'] # misc self.lastWindowClosed.connect(self.shutdown) @@ -423,10 +424,10 @@ class Application(QApplication): Qt.DirectConnection) # config - self.config.style_changed.connect(style.get_stylesheet.cache_clear) + config_obj.style_changed.connect(style.get_stylesheet.cache_clear) for obj in (tabs, completion, mainwin, cmd_history, websettings, modeman, status, status.txt): - self.config.changed.connect(obj.on_config_changed) + config_obj.changed.connect(obj.on_config_changed) for obj in kp.values(): self.keyconfig.changed.connect(obj.on_keyconfig_changed) @@ -767,11 +768,16 @@ class Application(QApplication): except KeyError: pass # Save everything - if hasattr(self, 'config') and self.config is not None: + try: + config_obj = self.registry['config'] + except KeyError: + log.destroy.debug("Config not initialized yet, so not saving " + "anything.") + else: to_save = [] - if self.config.get('general', 'auto-save-config'): + if config.get('general', 'auto-save-config'): if hasattr(self, 'config'): - to_save.append(("config", self.config.save)) + to_save.append(("config", config_obj.save)) if hasattr(self, 'keyconfig'): to_save.append(("keyconfig", self.keyconfig.save)) to_save += [("window geometry", self._save_geometry), @@ -802,9 +808,6 @@ class Application(QApplication): except AttributeError as e: log.destroy.warning("Could not save {}.".format(what)) log.destroy.debug(e) - else: - log.destroy.debug("Config not initialized yet, so not saving " - "anything.") # Re-enable faulthandler to stdout, then remove crash log log.destroy.debug("Deactiving crash log...") self._destroy_crashlogfile() diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index 0f94660b1..5e28d5997 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -30,28 +30,23 @@ import functools import configparser import collections.abc -from PyQt5.QtCore import pyqtSignal, QObject, QCoreApplication +from PyQt5.QtCore import pyqtSignal, QObject from qutebrowser.utils import log from qutebrowser.config import configdata, iniparsers, configtypes, textwrapper from qutebrowser.commands import cmdexc, cmdutils -from qutebrowser.utils import message +from qutebrowser.utils import message, utils from qutebrowser.utils.usertypes import Completion -def instance(): - """Get the global config instance.""" - return QCoreApplication.instance().config - - def get(*args, **kwargs): """Convenience method to call get(...) of the config instance.""" - return instance().get(*args, **kwargs) + return utils.get_object('config').get(*args, **kwargs) def section(sect): """Get a config section from the global config.""" - return instance()[sect] + return utils.get_object('config')[sect] class NoSectionError(configparser.NoSectionError): diff --git a/qutebrowser/config/style.py b/qutebrowser/config/style.py index 01000e570..12c21c739 100644 --- a/qutebrowser/config/style.py +++ b/qutebrowser/config/style.py @@ -25,7 +25,7 @@ import jinja2 from PyQt5.QtGui import QColor from qutebrowser.config import config -from qutebrowser.utils import log +from qutebrowser.utils import log, utils @functools.lru_cache(maxsize=16) @@ -42,7 +42,7 @@ def get_stylesheet(template_str): fontdict = FontDict(config.section('fonts')) template = jinja2.Template(template_str) return template.render(color=colordict, font=fontdict, - config=config.instance()) + config=utils.get_object('config')) def set_register_stylesheet(obj): @@ -60,7 +60,7 @@ def set_register_stylesheet(obj): log.style.vdebug("stylesheet for {}: {}".format( obj.__class__.__name__, qss)) obj.setStyleSheet(qss) - config.instance().changed.connect( + utils.get_object('config').changed.connect( functools.partial(_update_stylesheet, obj)) diff --git a/qutebrowser/utils/completer.py b/qutebrowser/utils/completer.py index d24d5c62c..22bf59577 100644 --- a/qutebrowser/utils/completer.py +++ b/qutebrowser/utils/completer.py @@ -23,7 +23,7 @@ from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject from qutebrowser.config import config, configdata from qutebrowser.commands import cmdutils -from qutebrowser.utils import usertypes, log +from qutebrowser.utils import usertypes, log, utils from qutebrowser.models import completion as models from qutebrowser.models.completionfilter import CompletionFilterModel as CFM @@ -69,6 +69,7 @@ class Completer(QObject): def _init_setting_completions(self): """Initialize setting completion models.""" + config_obj = utils.get_object('config') self._models[usertypes.Completion.section] = CFM( models.SettingSectionCompletionModel(self), self) self._models[usertypes.Completion.option] = {} @@ -77,13 +78,13 @@ class Completer(QObject): model = models.SettingOptionCompletionModel(sectname, self) self._models[usertypes.Completion.option][sectname] = CFM( model, self) - config.instance().changed.connect(model.on_config_changed) + config_obj.changed.connect(model.on_config_changed) self._models[usertypes.Completion.value][sectname] = {} for opt in configdata.DATA[sectname].keys(): model = models.SettingValueCompletionModel(sectname, opt, self) self._models[usertypes.Completion.value][sectname][opt] = CFM( model, self) - config.instance().changed.connect(model.on_config_changed) + config_obj.changed.connect(model.on_config_changed) def _get_new_completion(self, parts, cursor_part): """Get a new completion model. From 90820126b4ba9a33fda263227901512770702950 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 22:29:03 +0200 Subject: [PATCH 36/79] Update comment --- qutebrowser/app.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 0abf62d48..e38e2eac3 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -55,7 +55,6 @@ class Application(QApplication): Attributes: registry: The object registry of global objects. meta_registry: The object registry of object registries. - mainwindow: The MainWindow QWidget. _args: ArgumentParser instance. _commandrunner: The main CommandRunner instance. _debugconsole: The ConsoleWidget for debugging. From fe5d9939b92675b4099f4643d5ff52cd49659655 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 22:31:26 +0200 Subject: [PATCH 37/79] Move debug console from app to utilcmd --- qutebrowser/app.py | 9 ++------- qutebrowser/utils/utilcmds.py | 8 +++++++- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index e38e2eac3..0b3d45d74 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -57,7 +57,6 @@ class Application(QApplication): meta_registry: The object registry of object registries. _args: ArgumentParser instance. _commandrunner: The main CommandRunner instance. - _debugconsole: The ConsoleWidget for debugging. _keyparsers: A mapping from modes to keyparsers. _shutting_down: True if we're currently shutting down. _quit_status: The current quitting status. @@ -136,7 +135,8 @@ class Application(QApplication): mainwin = mainwindow.MainWindow() self.registry['mainwindow'] = mainwin log.init.debug("Initializing debug console...") - self._debugconsole = console.ConsoleWidget() + debug_console = console.ConsoleWidget() + self.registry['debug-console'] = debug_console log.init.debug("Initializing eventfilter...") self.installEventFilter(modeman_obj) self.setQuitOnLastWindowClosed(False) @@ -681,11 +681,6 @@ class Application(QApplication): self._crashdlg = crash.ReportDialog(pages, history, objects) self._crashdlg.show() - @cmdutils.register(instance='app', debug=True, name='debug-console') - def show_debugconsole(self): - """Show the debugging console.""" - self._debugconsole.show() - def interrupt(self, signum, _frame): """Handler for signals to gracefully shutdown (SIGINT/SIGTERM). diff --git a/qutebrowser/utils/utilcmds.py b/qutebrowser/utils/utilcmds.py index faaf8dbab..cf403f5c6 100644 --- a/qutebrowser/utils/utilcmds.py +++ b/qutebrowser/utils/utilcmds.py @@ -25,7 +25,7 @@ import functools from PyQt5.QtCore import QCoreApplication -from qutebrowser.utils import usertypes, log +from qutebrowser.utils import usertypes, log, utils from qutebrowser.commands import runners, cmdexc, cmdutils from qutebrowser.config import config, style @@ -100,3 +100,9 @@ def debug_cache_stats(): style_info = style.get_stylesheet.cache_info() log.misc.debug('config: {}'.format(config_info)) log.misc.debug('style: {}'.format(style_info)) + + +@cmdutils.register(debug=True) +def debug_console(): + """Show the debugging console.""" + utils.get_object('debug-console').show() From 04be586bca5b66352c3d049316c431755807e1c4 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 22:37:41 +0200 Subject: [PATCH 38/79] Use object registry for keyconfig. --- qutebrowser/app.py | 20 ++++++++++++-------- qutebrowser/keyinput/basekeyparser.py | 4 ++-- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 0b3d45d74..b8408d1ca 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -91,7 +91,6 @@ class Application(QApplication): self._keyparsers = None self._crashdlg = None self._crashlogfile = None - self.keyconfig = None sys.excepthook = self._exception_hook @@ -190,8 +189,7 @@ class Application(QApplication): else: self.registry['config'] = config_obj try: - self.keyconfig = keyconfparser.KeyConfigParser( - confdir, 'keys.conf') + keyconfig = keyconfparser.KeyConfigParser(confdir, 'keys.conf') except keyconfparser.KeyConfigError as e: log.init.exception(e) errstr = "Error while reading key config:\n" @@ -203,6 +201,8 @@ class Application(QApplication): msgbox.exec_() # We didn't really initialize much so far, so we just quit hard. sys.exit(1) + else: + self.registry['keyconfig'] = keyconfig stateconfig = iniparsers.ReadWriteConfigParser(confdir, 'state') self.registry['stateconfig'] = stateconfig cmd_history = lineparser.LineConfigParser( @@ -385,6 +385,7 @@ class Application(QApplication): cmd_history = self.registry['cmd_history'] downloadmanager = self.registry['downloadmanager'] config_obj = self.registry['config'] + keyconfig = self.registry['keyconfig'] # misc self.lastWindowClosed.connect(self.shutdown) @@ -428,7 +429,7 @@ class Application(QApplication): websettings, modeman, status, status.txt): config_obj.changed.connect(obj.on_config_changed) for obj in kp.values(): - self.keyconfig.changed.connect(obj.on_keyconfig_changed) + keyconfig.changed.connect(obj.on_keyconfig_changed) # statusbar # FIXME some of these probably only should be triggered on mainframe @@ -770,10 +771,13 @@ class Application(QApplication): else: to_save = [] if config.get('general', 'auto-save-config'): - if hasattr(self, 'config'): - to_save.append(("config", config_obj.save)) - if hasattr(self, 'keyconfig'): - to_save.append(("keyconfig", self.keyconfig.save)) + to_save.append(("config", config_obj.save)) + try: + keyconfig = self.registry['keyconfig'] + except KeyError: + pass + else: + to_save.append(("keyconfig", keyconfig.save)) to_save += [("window geometry", self._save_geometry), ("quickmarks", quickmarks.save)] try: diff --git a/qutebrowser/keyinput/basekeyparser.py b/qutebrowser/keyinput/basekeyparser.py index bffec833e..65ef08909 100644 --- a/qutebrowser/keyinput/basekeyparser.py +++ b/qutebrowser/keyinput/basekeyparser.py @@ -23,7 +23,7 @@ import re import string import functools -from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QObject, QCoreApplication +from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QObject from qutebrowser.config import config from qutebrowser.utils import usertypes, log, utils @@ -321,7 +321,7 @@ class BaseKeyParser(QObject): self._modename = modename self.bindings = {} self.special_bindings = {} - keyconfparser = QCoreApplication.instance().keyconfig + keyconfparser = utils.get_object('keyconfig') for (key, cmd) in keyconfparser.get_bindings_for(modename).items(): if not cmd: continue From 2b60cdb64ccd97c5a4bcc6df7ea781e7b1c7dd7d Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 23:05:55 +0200 Subject: [PATCH 39/79] Object names cleanup --- qutebrowser/app.py | 188 +++++++++++----------- qutebrowser/browser/downloads.py | 4 +- qutebrowser/browser/hints.py | 8 +- qutebrowser/commands/argparser.py | 2 +- qutebrowser/commands/command.py | 2 +- qutebrowser/commands/runners.py | 6 +- qutebrowser/config/keyconfparser.py | 4 +- qutebrowser/keyinput/basekeyparser.py | 2 +- qutebrowser/keyinput/modeman.py | 14 +- qutebrowser/models/downloadmodel.py | 16 +- qutebrowser/network/networkmanager.py | 6 +- qutebrowser/utils/message.py | 14 +- qutebrowser/utils/readline.py | 26 +-- qutebrowser/utils/usertypes.py | 4 - qutebrowser/widgets/mainwindow.py | 20 +-- qutebrowser/widgets/statusbar/bar.py | 6 +- qutebrowser/widgets/statusbar/command.py | 12 +- qutebrowser/widgets/statusbar/prompter.py | 6 +- qutebrowser/widgets/tabwidget.py | 2 +- qutebrowser/widgets/webview.py | 4 +- 20 files changed, 171 insertions(+), 175 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index b8408d1ca..8395f54aa 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -105,7 +105,7 @@ class Application(QApplication): self._handle_segfault() log.init.debug("Initializing modes...") self._init_modes() - modeman_obj = self.registry['modeman'] + mode_manager = self.registry['mode-manager'] log.init.debug("Initializing websettings...") websettings.init() log.init.debug("Initializing quickmarks...") @@ -117,27 +117,27 @@ class Application(QApplication): log.init.debug("Initializing utility commands...") utilcmds.init() log.init.debug("Initializing cookies...") - cookiejar = cookies.CookieJar(self) - self.registry['cookiejar'] = cookiejar + cookie_jar = cookies.CookieJar(self) + self.registry['cookie-jar'] = cookie_jar log.init.debug("Initializing cache...") diskcache = cache.DiskCache(self) self.registry['cache'] = diskcache log.init.debug("Initializing commands...") self._commandrunner = runners.CommandRunner() log.init.debug("Initializing search...") - searchrunner = runners.SearchRunner(self) - self.registry['searchrunner'] = searchrunner + search_runner = runners.SearchRunner(self) + self.registry['search-runner'] = search_runner log.init.debug("Initializing downloads...") - downloadmanager = downloads.DownloadManager(self) - self.registry['downloadmanager'] = downloadmanager + download_manager = downloads.DownloadManager(self) + self.registry['download-manager'] = download_manager log.init.debug("Initializing main window...") - mainwin = mainwindow.MainWindow() - self.registry['mainwindow'] = mainwin + main_window = mainwindow.MainWindow() + self.registry['main-window'] = main_window log.init.debug("Initializing debug console...") debug_console = console.ConsoleWidget() self.registry['debug-console'] = debug_console log.init.debug("Initializing eventfilter...") - self.installEventFilter(modeman_obj) + self.installEventFilter(mode_manager) self.setQuitOnLastWindowClosed(False) log.init.debug("Connecting signals...") @@ -146,7 +146,7 @@ class Application(QApplication): log.init.debug("Showing mainwindow...") if not args.nowindow: - mainwin.show() + main_window.show() log.init.debug("Applying python hacks...") self._python_hacks() QTimer.singleShot(0, self._process_init_args) @@ -189,7 +189,7 @@ class Application(QApplication): else: self.registry['config'] = config_obj try: - keyconfig = keyconfparser.KeyConfigParser(confdir, 'keys.conf') + key_config = keyconfparser.KeyConfigParser(confdir, 'keys.conf') except keyconfparser.KeyConfigError as e: log.init.exception(e) errstr = "Error while reading key config:\n" @@ -202,12 +202,12 @@ class Application(QApplication): # We didn't really initialize much so far, so we just quit hard. sys.exit(1) else: - self.registry['keyconfig'] = keyconfig - stateconfig = iniparsers.ReadWriteConfigParser(confdir, 'state') - self.registry['stateconfig'] = stateconfig - cmd_history = lineparser.LineConfigParser( + self.registry['key-config'] = key_config + state_config = iniparsers.ReadWriteConfigParser(confdir, 'state') + self.registry['state-config'] = state_config + command_history = lineparser.LineConfigParser( confdir, 'cmd_history', ('completion', 'history-length')) - self.registry['cmd_history'] = cmd_history + self.registry['command-history'] = command_history def _init_modes(self): """Inizialize the mode manager and the keyparsers.""" @@ -227,27 +227,27 @@ class Application(QApplication): utypes.KeyMode.yesno: modeparsers.PromptKeyParser(self), } - modeman_obj = modeman.ModeManager(self) - self.registry['modeman'] = modeman_obj - modeman_obj.register(utypes.KeyMode.normal, - self._keyparsers[utypes.KeyMode.normal].handle) - modeman_obj.register(utypes.KeyMode.hint, - self._keyparsers[utypes.KeyMode.hint].handle) - modeman_obj.register(utypes.KeyMode.insert, - self._keyparsers[utypes.KeyMode.insert].handle, - passthrough=True) - modeman_obj.register( + mode_manager = modeman.ModeManager(self) + self.registry['mode-manager'] = mode_manager + mode_manager.register(utypes.KeyMode.normal, + self._keyparsers[utypes.KeyMode.normal].handle) + mode_manager.register(utypes.KeyMode.hint, + self._keyparsers[utypes.KeyMode.hint].handle) + mode_manager.register(utypes.KeyMode.insert, + self._keyparsers[utypes.KeyMode.insert].handle, + passthrough=True) + mode_manager.register( utypes.KeyMode.passthrough, self._keyparsers[utypes.KeyMode.passthrough].handle, passthrough=True) - modeman_obj.register(utypes.KeyMode.command, - self._keyparsers[utypes.KeyMode.command].handle, - passthrough=True) - modeman_obj.register(utypes.KeyMode.prompt, - self._keyparsers[utypes.KeyMode.prompt].handle, - passthrough=True) - modeman_obj.register(utypes.KeyMode.yesno, - self._keyparsers[utypes.KeyMode.yesno].handle) + mode_manager.register(utypes.KeyMode.command, + self._keyparsers[utypes.KeyMode.command].handle, + passthrough=True) + mode_manager.register(utypes.KeyMode.prompt, + self._keyparsers[utypes.KeyMode.prompt].handle, + passthrough=True) + mode_manager.register(utypes.KeyMode.yesno, + self._keyparsers[utypes.KeyMode.yesno].handle) def _init_misc(self): """Initialize misc things.""" @@ -262,10 +262,10 @@ class Application(QApplication): self.setOrganizationName("qutebrowser") self.setApplicationName("qutebrowser") self.setApplicationVersion(qutebrowser.__version__) - messagebridge = message.MessageBridge(self) - self.registry['messagebridge'] = messagebridge - rl_bridge = readline.ReadlineBridge() - self.registry['rl_bridge'] = rl_bridge + message_bridge = message.MessageBridge(self) + self.registry['message-bridge'] = message_bridge + readline_bridge = readline.ReadlineBridge() + self.registry['readline-bridge'] = readline_bridge def _handle_segfault(self): """Handle a segfault from a previous run.""" @@ -328,7 +328,7 @@ class Application(QApplication): # we make sure the GUI is refreshed here, so the start seems faster. self.processEvents(QEventLoop.ExcludeUserInputEvents | QEventLoop.ExcludeSocketNotifiers) - tabbedbrowser = self.registry['tabbedbrowser'] + tabbed_browser = self.registry['tabbed-browser'] for cmd in self._args.command: if cmd.startswith(':'): log.init.debug("Startup cmd {}".format(cmd)) @@ -341,9 +341,9 @@ class Application(QApplication): message.error("Error in startup argument '{}': {}".format( cmd, e)) else: - tabbedbrowser.tabopen(url) + tabbed_browser.tabopen(url) - if tabbedbrowser.count() == 0: + if tabbed_browser.count() == 0: log.init.debug("Opening startpage") for urlstr in config.get('general', 'startpage'): try: @@ -351,7 +351,7 @@ class Application(QApplication): except urlutils.FuzzyUrlError as e: message.error("Error when opening startpage: {}".format(e)) else: - tabbedbrowser.tabopen(url) + tabbed_browser.tabopen(url) def _python_hacks(self): """Get around some PyQt-oddities by evil hacks. @@ -365,44 +365,44 @@ class Application(QApplication): timer = utypes.Timer(self, 'python_hacks') timer.start(500) timer.timeout.connect(lambda: None) - self.registry['python_hack_timer'] = timer + self.registry['python-hack-timer'] = timer def _connect_signals(self): """Connect all signals to their slots.""" # pylint: disable=too-many-statements # syntactic sugar kp = self._keyparsers - mainwin = self.registry['mainwindow'] - status = mainwin.status + main_window = self.registry['main-window'] + status = main_window.status completion = self.registry['completion'] - tabs = self.registry['tabbedbrowser'] - cmd = self.registry['status-cmd'] + tabs = self.registry['tabbed-browser'] + cmd = self.registry['status-command'] completer = self.registry['completer'] - searchrunner = self.registry['searchrunner'] - messagebridge = self.registry['messagebridge'] - modeman = self.registry['modeman'] + search_runner = self.registry['search-runner'] + message_bridge = self.registry['message-bridge'] + mode_manager = self.registry['mode-manager'] prompter = self.registry['prompter'] - cmd_history = self.registry['cmd_history'] - downloadmanager = self.registry['downloadmanager'] + command_history = self.registry['command-history'] + download_manager = self.registry['download-manager'] config_obj = self.registry['config'] - keyconfig = self.registry['keyconfig'] + key_config = self.registry['key-config'] # misc self.lastWindowClosed.connect(self.shutdown) tabs.quit.connect(self.shutdown) # status bar - modeman.entered.connect(status.on_mode_entered) - modeman.left.connect(status.on_mode_left) - modeman.left.connect(cmd.on_mode_left) - modeman.left.connect(prompter.on_mode_left) + mode_manager.entered.connect(status.on_mode_entered) + mode_manager.left.connect(status.on_mode_left) + mode_manager.left.connect(cmd.on_mode_left) + mode_manager.left.connect(prompter.on_mode_left) # commands cmd.got_cmd.connect(self._commandrunner.run_safely) - cmd.got_search.connect(searchrunner.search) - cmd.got_search_rev.connect(searchrunner.search_rev) + cmd.got_search.connect(search_runner.search) + cmd.got_search_rev.connect(search_runner.search_rev) cmd.returnPressed.connect(tabs.setFocus) - searchrunner.do_search.connect(tabs.search) + search_runner.do_search.connect(tabs.search) kp[utypes.KeyMode.normal].keystring_updated.connect( status.keystring.setText) tabs.got_cmd.connect(self._commandrunner.run_safely) @@ -415,21 +415,21 @@ class Application(QApplication): kp[utypes.KeyMode.hint].on_hint_strings_updated) # messages - messagebridge.s_error.connect(status.disp_error) - messagebridge.s_info.connect(status.disp_temp_text) - messagebridge.s_set_text.connect(status.set_text) - messagebridge.s_maybe_reset_text.connect(status.txt.maybe_reset_text) - messagebridge.s_set_cmd_text.connect(cmd.set_cmd_text) - messagebridge.s_question.connect(prompter.ask_question, - Qt.DirectConnection) + message_bridge.s_error.connect(status.disp_error) + message_bridge.s_info.connect(status.disp_temp_text) + message_bridge.s_set_text.connect(status.set_text) + message_bridge.s_maybe_reset_text.connect(status.txt.maybe_reset_text) + message_bridge.s_set_cmd_text.connect(cmd.set_cmd_text) + message_bridge.s_question.connect(prompter.ask_question, + Qt.DirectConnection) # config config_obj.style_changed.connect(style.get_stylesheet.cache_clear) - for obj in (tabs, completion, mainwin, cmd_history, - websettings, modeman, status, status.txt): + for obj in (tabs, completion, main_window, command_history, + websettings, mode_manager, status, status.txt): config_obj.changed.connect(obj.on_config_changed) for obj in kp.values(): - keyconfig.changed.connect(obj.on_keyconfig_changed) + key_config.changed.connect(obj.on_keyconfig_changed) # statusbar # FIXME some of these probably only should be triggered on mainframe @@ -452,7 +452,7 @@ class Application(QApplication): tabs.cur_load_status_changed.connect(status.url.on_load_status_changed) # command input / completion - modeman.left.connect(tabs.on_mode_left) + mode_manager.left.connect(tabs.on_mode_left) cmd.clear_completion_selection.connect( completion.on_clear_completion_selection) cmd.hide_completion.connect(completion.hide) @@ -460,8 +460,8 @@ class Application(QApplication): completer.change_completed_part.connect(cmd.on_change_completed_part) # downloads - tabs.start_download.connect(downloadmanager.fetch) - tabs.download_get.connect(downloadmanager.get) + tabs.start_download.connect(download_manager.fetch) + tabs.download_get.connect(download_manager.get) def _get_widgets(self): """Get a string list of all widgets.""" @@ -516,11 +516,11 @@ class Application(QApplication): A list of open pages, or an empty list. """ try: - tabbedbrowser = self.registry['tabbedbrowser'] + tabbed_browser = self.registry['tabbed-browser'] except KeyError: return [] pages = [] - for tab in tabbedbrowser.widgets(): + for tab in tabbed_browser.widgets(): try: url = tab.cur_url.toString( QUrl.RemovePassword | QUrl.FullyEncoded) @@ -532,14 +532,14 @@ class Application(QApplication): def _save_geometry(self): """Save the window geometry to the state config.""" - stateconfig = self.registry['stateconfig'] - data = bytes(self.registry['mainwindow'].saveGeometry()) + state_config = self.registry['state-config'] + data = bytes(self.registry['main-window'].saveGeometry()) geom = base64.b64encode(data).decode('ASCII') try: - stateconfig.add_section('geometry') + state_config.add_section('geometry') except configparser.DuplicateSectionError: pass - stateconfig['geometry']['mainwindow'] = geom + state_config['geometry']['mainwindow'] = geom def _destroy_crashlogfile(self): """Clean up the crash log file and delete it.""" @@ -588,7 +588,7 @@ class Application(QApplication): pages = [] try: - history = self.registry['status-cmd'].history[-5:] + history = self.registry['status-command'].history[-5:] except Exception: log.destroy.exception("Error while getting history: {}") history = [] @@ -623,7 +623,7 @@ class Application(QApplication): # exceptions occur. if pages is None: pages = [] - for tab in utils.get_object('tabbedbrowser').widgets(): + for tab in utils.get_object('tabbed-browser').widgets(): urlstr = tab.cur_url.toString( QUrl.RemovePassword | QUrl.FullyEncoded) if urlstr: @@ -670,14 +670,14 @@ class Application(QApplication): except Exception: # pylint: disable=broad-except out = traceback.format_exc() qutescheme.pyeval_output = out - self.registry['tabbedbrowser'].openurl( + self.registry['tabbed-browser'].openurl( QUrl('qute:pyeval'), newtab=True) @cmdutils.register(instance='app') def report(self): """Report a bug in qutebrowser.""" pages = self._recover_pages() - history = self.registry['status-cmd'].history[-5:] + history = self.registry['status-command'].history[-5:] objects = self.get_all_objects() self._crashdlg = crash.ReportDialog(pages, history, objects) self._crashdlg.show() @@ -753,13 +753,13 @@ class Application(QApplication): # Remove eventfilter try: log.destroy.debug("Removing eventfilter...") - self.removeEventFilter(self.registry['modeman']) + self.removeEventFilter(self.registry['mode-manager']) except KeyError: pass # Close all tabs try: log.destroy.debug("Closing tabs...") - self.registry['tabbedbrowser'].shutdown() + self.registry['tabbed-browser'].shutdown() except KeyError: pass # Save everything @@ -773,31 +773,31 @@ class Application(QApplication): if config.get('general', 'auto-save-config'): to_save.append(("config", config_obj.save)) try: - keyconfig = self.registry['keyconfig'] + key_config = self.registry['key-config'] except KeyError: pass else: - to_save.append(("keyconfig", keyconfig.save)) + to_save.append(("keyconfig", key_config.save)) to_save += [("window geometry", self._save_geometry), ("quickmarks", quickmarks.save)] try: - cmd_history = self.registry['cmd_history'] + command_history = self.registry['command-history'] except KeyError: pass else: - to_save.append(("command history", cmd_history.save)) + to_save.append(("command history", command_history.save)) try: - stateconfig = self.registry['stateconfig'] + state_config = self.registry['state-config'] except KeyError: pass else: - to_save.append(("window geometry", stateconfig.save)) + to_save.append(("window geometry", state_config.save)) try: - cookiejar = self.registry['cookiejar'] + cookie_jar = self.registry['cookie-jar'] except KeyError: pass else: - to_save.append(("cookies", cookiejar.save)) + to_save.append(("cookies", cookie_jar.save)) for what, handler in to_save: log.destroy.debug("Saving {} (handler: {})".format( what, handler.__qualname__)) diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py index 99ccf674e..bc9f21339 100644 --- a/qutebrowser/browser/downloads.py +++ b/qutebrowser/browser/downloads.py @@ -363,7 +363,7 @@ class DownloadManager(QObject): reply = page.networkAccessManager().get(req) self.fetch(reply) - @cmdutils.register(instance='downloadmanager') + @cmdutils.register(instance='download-manager') def cancel_download(self, count=1): """Cancel the first/[count]th download. @@ -409,7 +409,7 @@ class DownloadManager(QObject): q.destroyed.connect(functools.partial(self.questions.remove, q)) self.questions.append(q) download.cancelled.connect(q.abort) - utils.get_object('messagebridge').ask(q, blocking=False) + utils.get_object('message-bridge').ask(q, blocking=False) @pyqtSlot(DownloadItem) def on_finished(self, download): diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index b4430c063..351e862e1 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -149,8 +149,8 @@ class HintManager(QObject): """ super().__init__(parent) self._context = None - utils.get_object('modeman').left.connect(self.on_mode_left) - utils.get_object('modeman').entered.connect(self.on_mode_entered) + utils.get_object('mode-manager').left.connect(self.on_mode_left) + utils.get_object('mode-manager').entered.connect(self.on_mode_entered) def _cleanup(self): """Clean up after hinting.""" @@ -160,7 +160,7 @@ class HintManager(QObject): except webelem.IsNullError: pass text = self.HINT_TEXTS[self._context.target] - utils.get_object('messagebridge').maybe_reset_text(text) + utils.get_object('message-bridge').maybe_reset_text(text) self._context = None def _hint_strings(self, elems): @@ -541,7 +541,7 @@ class HintManager(QObject): self._context.frames = webelem.get_child_frames(mainframe) self._context.args = args self._init_elements(mainframe, group) - utils.get_object('messagebridge').set_text(self.HINT_TEXTS[target]) + utils.get_object('message-bridge').set_text(self.HINT_TEXTS[target]) self._connect_frame_signals() try: modeman.enter(usertypes.KeyMode.hint, 'HintManager.start') diff --git a/qutebrowser/commands/argparser.py b/qutebrowser/commands/argparser.py index 3919561cb..a92a1599a 100644 --- a/qutebrowser/commands/argparser.py +++ b/qutebrowser/commands/argparser.py @@ -54,7 +54,7 @@ class HelpAction(argparse.Action): """ def __call__(self, parser, _namespace, _values, _option_string=None): - utils.get_object('tabbedbrowser').tabopen( + utils.get_object('tabbed-browser').tabopen( QUrl('qute://help/commands.html#{}'.format(parser.name))) parser.exit() diff --git a/qutebrowser/commands/command.py b/qutebrowser/commands/command.py index 825a5b9f0..3e65d69bc 100644 --- a/qutebrowser/commands/command.py +++ b/qutebrowser/commands/command.py @@ -97,7 +97,7 @@ class Command: Raise: PrerequisitesError if the command can't be called currently. """ - curmode = utils.get_object('modeman').mode() + curmode = utils.get_object('mode-manager').mode() if self.modes is not None and curmode not in self.modes: mode_names = '/'.join(mode.name for mode in self.modes) raise cmdexc.PrerequisitesError( diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py index 6d710a59c..2b934222f 100644 --- a/qutebrowser/commands/runners.py +++ b/qutebrowser/commands/runners.py @@ -32,7 +32,7 @@ def replace_variables(arglist): args = [] for arg in arglist: if arg == '{url}': - url = utils.get_object('tabbedbrowser').current_url().toString( + url = utils.get_object('tabbed-browser').current_url().toString( QUrl.FullyEncoded | QUrl.RemovePassword) args.append(url) else: @@ -114,7 +114,7 @@ class SearchRunner(QObject): """ self._search(text, rev=True) - @cmdutils.register(instance='searchrunner', hide=True) + @cmdutils.register(instance='search-runner', hide=True) def search_next(self, count=1): """Continue the search to the ([count]th) next term. @@ -128,7 +128,7 @@ class SearchRunner(QObject): for _ in range(count): self.do_search.emit(self._text, self._flags) - @cmdutils.register(instance='searchrunner', hide=True) + @cmdutils.register(instance='search-runner', hide=True) def search_prev(self, count=1): """Continue the search to the ([count]th) previous term. diff --git a/qutebrowser/config/keyconfparser.py b/qutebrowser/config/keyconfparser.py index 28a2d6495..8545c7764 100644 --- a/qutebrowser/config/keyconfparser.py +++ b/qutebrowser/config/keyconfparser.py @@ -119,7 +119,7 @@ class KeyConfigParser(QObject): with open(self._configfile, 'w', encoding='utf-8') as f: f.write(str(self)) - @cmdutils.register(instance='keyconfig') + @cmdutils.register(instance='key-config') def bind(self, key, *command, mode=None): """Bind a key to a command. @@ -144,7 +144,7 @@ class KeyConfigParser(QObject): for m in mode.split(','): self.changed.emit(m) - @cmdutils.register(instance='keyconfig') + @cmdutils.register(instance='key-config') def unbind(self, key, mode=None): """Unbind a keychain. diff --git a/qutebrowser/keyinput/basekeyparser.py b/qutebrowser/keyinput/basekeyparser.py index 65ef08909..ebfb51d81 100644 --- a/qutebrowser/keyinput/basekeyparser.py +++ b/qutebrowser/keyinput/basekeyparser.py @@ -321,7 +321,7 @@ class BaseKeyParser(QObject): self._modename = modename self.bindings = {} self.special_bindings = {} - keyconfparser = utils.get_object('keyconfig') + keyconfparser = utils.get_object('key-config') for (key, cmd) in keyconfparser.get_bindings_for(modename).items(): if not cmd: continue diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index 3912717da..22a561d8b 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -39,18 +39,18 @@ class ModeLockedError(Exception): def enter(mode, reason=None): """Enter the mode 'mode'.""" - utils.get_object('modeman').enter(mode, reason) + utils.get_object('mode-manager').enter(mode, reason) def leave(mode, reason=None): """Leave the mode 'mode'.""" - utils.get_object('modeman').leave(mode, reason) + utils.get_object('mode-manager').leave(mode, reason) def maybe_enter(mode, reason=None): """Convenience method to enter 'mode' without exceptions.""" try: - utils.get_object('modeman').enter(mode, reason) + utils.get_object('mode-manager').enter(mode, reason) except ModeLockedError: pass @@ -58,7 +58,7 @@ def maybe_enter(mode, reason=None): def maybe_leave(mode, reason=None): """Convenience method to leave 'mode' without exceptions.""" try: - utils.get_object('modeman').leave(mode, reason) + utils.get_object('mode-manager').leave(mode, reason) except ValueError as e: # This is rather likely to happen, so we only log to debug log. log.modes.debug(e) @@ -212,7 +212,7 @@ class ModeManager(QObject): log.modes.debug("New mode stack: {}".format(self._mode_stack)) self.entered.emit(mode) - @cmdutils.register(instance='modeman', hide=True) + @cmdutils.register(instance='mode-manager', hide=True) def enter_mode(self, mode): """Enter a key mode. @@ -245,7 +245,7 @@ class ModeManager(QObject): self._mode_stack)) self.left.emit(mode) - @cmdutils.register(instance='modeman', name='leave-mode', + @cmdutils.register(instance='mode-manager', name='leave-mode', not_modes=[usertypes.KeyMode.normal], hide=True) def leave_current_mode(self): """Leave the mode we're currently in.""" @@ -283,7 +283,7 @@ class ModeManager(QObject): # we're not interested in it anymore. return False if (QApplication.instance().activeWindow() is not - utils.get_object('mainwindow')): + utils.get_object('main-window')): # Some other window (print dialog, etc.) is focused so we pass # the event through. return False diff --git a/qutebrowser/models/downloadmodel.py b/qutebrowser/models/downloadmodel.py index f2495d2ff..a99bb8188 100644 --- a/qutebrowser/models/downloadmodel.py +++ b/qutebrowser/models/downloadmodel.py @@ -39,14 +39,14 @@ class DownloadModel(QAbstractListModel): def __init__(self, parent=None): super().__init__(parent) - downloadmanager = utils.get_object('downloadmanager') - downloadmanager.download_about_to_be_added.connect( + download_manager = utils.get_object('download-manager') + download_manager.download_about_to_be_added.connect( lambda idx: self.beginInsertRows(QModelIndex(), idx, idx)) - downloadmanager.download_added.connect(self.endInsertRows) - downloadmanager.download_about_to_be_finished.connect( + download_manager.download_added.connect(self.endInsertRows) + download_manager.download_about_to_be_finished.connect( lambda idx: self.beginRemoveRows(QModelIndex(), idx, idx)) - downloadmanager.download_finished.connect(self.endRemoveRows) - downloadmanager.data_changed.connect(self.on_data_changed) + download_manager.download_finished.connect(self.endRemoveRows) + download_manager.data_changed.connect(self.on_data_changed) def __repr__(self): return '<{}>'.format(self.__class__.__name__) @@ -81,7 +81,7 @@ class DownloadModel(QAbstractListModel): if index.parent().isValid() or index.column() != 0: return QVariant() - item = utils.get_object('downloadmanager').downloads[index.row()] + item = utils.get_object('download-manager').downloads[index.row()] if role == Qt.DisplayRole: data = str(item) elif role == Qt.ForegroundRole: @@ -105,4 +105,4 @@ class DownloadModel(QAbstractListModel): if parent.isValid(): # We don't have children return 0 - return len(utils.get_object('downloadmanager').downloads) + return len(utils.get_object('download-manager').downloads) diff --git a/qutebrowser/network/networkmanager.py b/qutebrowser/network/networkmanager.py index bb7eaa708..7bcf6d665 100644 --- a/qutebrowser/network/networkmanager.py +++ b/qutebrowser/network/networkmanager.py @@ -55,9 +55,9 @@ class NetworkManager(QNetworkAccessManager): # We have a shared cookie jar and cache - we restore their parents so # we don't take ownership of them. app = QCoreApplication.instance() - cookiejar = utils.get_object('cookiejar') - self.setCookieJar(cookiejar) - cookiejar.setParent(app) + cookie_jar = utils.get_object('cookie-jar') + self.setCookieJar(cookie_jar) + cookie_jar.setParent(app) cache = utils.get_object('cache') self.setCache(cache) cache.setParent(app) diff --git a/qutebrowser/utils/message.py b/qutebrowser/utils/message.py index 22b1d86e0..7fec60d4c 100644 --- a/qutebrowser/utils/message.py +++ b/qutebrowser/utils/message.py @@ -30,7 +30,7 @@ def error(message, immediately=False): Args: See MessageBridge.error. """ - utils.get_object('messagebridge').error(message, immediately) + utils.get_object('message-bridge').error(message, immediately) def info(message, immediately=True): @@ -39,12 +39,12 @@ def info(message, immediately=True): Args: See MessageBridge.info. """ - utils.get_object('messagebridge').info(message, immediately) + utils.get_object('message-bridge').info(message, immediately) def set_cmd_text(txt): """Convienience function to Set the statusbar command line to a text.""" - utils.get_object('messagebridge').set_cmd_text(txt) + utils.get_object('message-bridge').set_cmd_text(txt) def ask(message, mode, default=None): @@ -62,7 +62,7 @@ def ask(message, mode, default=None): q.text = message q.mode = mode q.default = default - utils.get_object('messagebridge').ask(q, blocking=True) + utils.get_object('message-bridge').ask(q, blocking=True) q.deleteLater() return q.answer @@ -72,7 +72,7 @@ def alert(message): q = usertypes.Question() q.text = message q.mode = usertypes.PromptMode.alert - utils.get_object('messagebridge').ask(q, blocking=True) + utils.get_object('message-bridge').ask(q, blocking=True) q.deleteLater() @@ -87,7 +87,7 @@ def ask_async(message, mode, handler, default=None): """ if not isinstance(mode, usertypes.PromptMode): raise TypeError("Mode {} is no PromptMode member!".format(mode)) - bridge = utils.get_object('messagebridge') + bridge = utils.get_object('message-bridge') q = usertypes.Question(bridge) q.text = message q.mode = mode @@ -106,7 +106,7 @@ def confirm_async(message, yes_action, no_action=None, default=None): no_action: Callable to be called when the user answered no. default: True/False to set a default value, or None. """ - bridge = utils.get_object('messagebridge') + bridge = utils.get_object('message-bridge') q = usertypes.Question(bridge) q.text = message q.mode = usertypes.PromptMode.yesno diff --git a/qutebrowser/utils/readline.py b/qutebrowser/utils/readline.py index 915e2ed36..e5e13672d 100644 --- a/qutebrowser/utils/readline.py +++ b/qutebrowser/utils/readline.py @@ -44,7 +44,7 @@ class ReadlineBridge: else: return None - @cmdutils.register(instance='rl_bridge', hide=True, + @cmdutils.register(instance='readline-bridge', hide=True, modes=[typ.KeyMode.command, typ.KeyMode.prompt]) def rl_backward_char(self): """Move back a character. @@ -56,7 +56,7 @@ class ReadlineBridge: return widget.cursorBackward(False) - @cmdutils.register(instance='rl_bridge', hide=True, + @cmdutils.register(instance='readline-bridge', hide=True, modes=[typ.KeyMode.command, typ.KeyMode.prompt]) def rl_forward_char(self): """Move forward a character. @@ -68,7 +68,7 @@ class ReadlineBridge: return widget.cursorForward(False) - @cmdutils.register(instance='rl_bridge', hide=True, + @cmdutils.register(instance='readline-bridge', hide=True, modes=[typ.KeyMode.command, typ.KeyMode.prompt]) def rl_backward_word(self): """Move back to the start of the current or previous word. @@ -80,7 +80,7 @@ class ReadlineBridge: return widget.cursorWordBackward(False) - @cmdutils.register(instance='rl_bridge', hide=True, + @cmdutils.register(instance='readline-bridge', hide=True, modes=[typ.KeyMode.command, typ.KeyMode.prompt]) def rl_forward_word(self): """Move forward to the end of the next word. @@ -92,7 +92,7 @@ class ReadlineBridge: return widget.cursorWordForward(False) - @cmdutils.register(instance='rl_bridge', hide=True, + @cmdutils.register(instance='readline-bridge', hide=True, modes=[typ.KeyMode.command, typ.KeyMode.prompt]) def rl_beginning_of_line(self): """Move to the start of the line. @@ -104,7 +104,7 @@ class ReadlineBridge: return widget.home(False) - @cmdutils.register(instance='rl_bridge', hide=True, + @cmdutils.register(instance='readline-bridge', hide=True, modes=[typ.KeyMode.command, typ.KeyMode.prompt]) def rl_end_of_line(self): """Move to the end of the line. @@ -116,7 +116,7 @@ class ReadlineBridge: return widget.end(False) - @cmdutils.register(instance='rl_bridge', hide=True, + @cmdutils.register(instance='readline-bridge', hide=True, modes=[typ.KeyMode.command, typ.KeyMode.prompt]) def rl_unix_line_discard(self): """Remove chars backward from the cursor to the beginning of the line. @@ -130,7 +130,7 @@ class ReadlineBridge: self.deleted[widget] = widget.selectedText() widget.del_() - @cmdutils.register(instance='rl_bridge', hide=True, + @cmdutils.register(instance='readline-bridge', hide=True, modes=[typ.KeyMode.command, typ.KeyMode.prompt]) def rl_kill_line(self): """Remove chars from the cursor to the end of the line. @@ -144,7 +144,7 @@ class ReadlineBridge: self.deleted[widget] = widget.selectedText() widget.del_() - @cmdutils.register(instance='rl_bridge', hide=True, + @cmdutils.register(instance='readline-bridge', hide=True, modes=[typ.KeyMode.command, typ.KeyMode.prompt]) def rl_unix_word_rubout(self): """Remove chars from the cursor to the beginning of the word. @@ -158,7 +158,7 @@ class ReadlineBridge: self.deleted[widget] = widget.selectedText() widget.del_() - @cmdutils.register(instance='rl_bridge', hide=True, + @cmdutils.register(instance='readline-bridge', hide=True, modes=[typ.KeyMode.command, typ.KeyMode.prompt]) def rl_kill_word(self): """Remove chars from the cursor to the end of the current word. @@ -172,7 +172,7 @@ class ReadlineBridge: self.deleted[widget] = widget.selectedText() widget.del_() - @cmdutils.register(instance='rl_bridge', hide=True, + @cmdutils.register(instance='readline-bridge', hide=True, modes=[typ.KeyMode.command, typ.KeyMode.prompt]) def rl_yank(self): """Paste the most recently deleted text. @@ -184,7 +184,7 @@ class ReadlineBridge: return widget.insert(self.deleted[widget]) - @cmdutils.register(instance='rl_bridge', hide=True, + @cmdutils.register(instance='readline-bridge', hide=True, modes=[typ.KeyMode.command, typ.KeyMode.prompt]) def rl_delete_char(self): """Delete the character after the cursor. @@ -196,7 +196,7 @@ class ReadlineBridge: return widget.del_() - @cmdutils.register(instance='rl_bridge', hide=True, + @cmdutils.register(instance='readline-bridge', hide=True, modes=[typ.KeyMode.command, typ.KeyMode.prompt]) def rl_backward_delete_char(self): """Delete the character before the cursor. diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index 947ea22a2..51ee611d2 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -390,10 +390,6 @@ class ObjectRegistry(collections.UserDict): """A registry of long-living objects in qutebrowser. Inspired by the eric IDE code (E5Gui/E5Application.py). - - Objects registered globally: - cookiejar: CookieJar instance. - cache: DiskCache instance. """ def __setitem__(self, name, obj): diff --git a/qutebrowser/widgets/mainwindow.py b/qutebrowser/widgets/mainwindow.py index a24a26804..4c32c0fe4 100644 --- a/qutebrowser/widgets/mainwindow.py +++ b/qutebrowser/widgets/mainwindow.py @@ -42,7 +42,7 @@ class MainWindow(QWidget): Attributes: status: The StatusBar widget. downloadview: The DownloadView widget. - _tabbedbrowser: The TabbedBrowser widget. + _tabbed_browser: The TabbedBrowser widget. _vbox: The main QVBoxLayout. """ @@ -50,9 +50,9 @@ class MainWindow(QWidget): super().__init__(parent) self.setWindowTitle('qutebrowser') - stateconf = utils.get_object('stateconfig') + state_config = utils.get_object('state-config') try: - data = stateconf['geometry']['mainwindow'] + data = state_config['geometry']['mainwindow'] log.init.debug("Restoring mainwindow from {}".format(data)) geom = base64.b64decode(data, validate=True) except KeyError: @@ -81,10 +81,10 @@ class MainWindow(QWidget): self._vbox.addWidget(self.downloadview) self.downloadview.show() - self._tabbedbrowser = tabbedbrowser.TabbedBrowser() - self._tabbedbrowser.title_changed.connect(self.setWindowTitle) - utils.register_object('tabbedbrowser', self._tabbedbrowser) - self._vbox.addWidget(self._tabbedbrowser) + self._tabbed_browser = tabbedbrowser.TabbedBrowser() + self._tabbed_browser.title_changed.connect(self.setWindowTitle) + utils.register_object('tabbed-browser', self._tabbed_browser) + self._vbox.addWidget(self._tabbed_browser) self._completion = completion.CompletionView(self) utils.register_object('completion', self._completion) @@ -145,7 +145,7 @@ class MainWindow(QWidget): if rect.isValid(): self._completion.setGeometry(rect) - @cmdutils.register(instance='mainwindow', name=['quit', 'q']) + @cmdutils.register(instance='main-window', name=['quit', 'q']) def close(self): """Quit qutebrowser. @@ -164,12 +164,12 @@ class MainWindow(QWidget): super().resizeEvent(e) self.resize_completion() self.downloadview.updateGeometry() - self._tabbedbrowser.tabBar().refresh() + self._tabbed_browser.tabBar().refresh() def closeEvent(self, e): """Override closeEvent to display a confirmation if needed.""" confirm_quit = config.get('ui', 'confirm-quit') - count = self._tabbedbrowser.count() + count = self._tabbed_browser.count() if confirm_quit == 'never': e.accept() elif confirm_quit == 'multiple-tabs' and count <= 1: diff --git a/qutebrowser/widgets/statusbar/bar.py b/qutebrowser/widgets/statusbar/bar.py index 08c23e213..9d7944e7c 100644 --- a/qutebrowser/widgets/statusbar/bar.py +++ b/qutebrowser/widgets/statusbar/bar.py @@ -133,7 +133,7 @@ class StatusBar(QWidget): self._stack.setContentsMargins(0, 0, 0, 0) self._cmd = command.Command() - utils.register_object('status-cmd', self._cmd) + utils.register_object('status-command', self._cmd) self._stack.addWidget(self._cmd) self.txt = textwidget.Text() @@ -377,7 +377,7 @@ class StatusBar(QWidget): @pyqtSlot(usertypes.KeyMode) def on_mode_entered(self, mode): """Mark certain modes in the commandline.""" - if mode in utils.get_object('modeman').passthrough: + if mode in utils.get_object('mode-manager').passthrough: text = "-- {} MODE --".format(mode.name.upper()) self.txt.set_text(self.txt.Text.normal, text) if mode == usertypes.KeyMode.insert: @@ -386,7 +386,7 @@ class StatusBar(QWidget): @pyqtSlot(usertypes.KeyMode) def on_mode_left(self, mode): """Clear marked mode.""" - if mode in utils.get_object('modeman').passthrough: + if mode in utils.get_object('mode-manager').passthrough: self.txt.set_text(self.txt.Text.normal, '') if mode == usertypes.KeyMode.insert: self._set_insert_active(False) diff --git a/qutebrowser/widgets/statusbar/command.py b/qutebrowser/widgets/statusbar/command.py index cc6d75525..d6c38935e 100644 --- a/qutebrowser/widgets/statusbar/command.py +++ b/qutebrowser/widgets/statusbar/command.py @@ -74,7 +74,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): misc.CommandLineEdit.__init__(self, parent) misc.MinimalLineEditMixin.__init__(self) self.cursor_part = 0 - self.history.history = utils.get_object('cmd_history').data + self.history.history = utils.get_object('command-history').data self._empty_item_idx = None self.textEdited.connect(self.on_text_edited) self.cursorPositionChanged.connect(self._update_cursor_part) @@ -160,7 +160,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): self.setFocus() self.show_cmd.emit() - @cmdutils.register(instance='status-cmd', name='set-cmd-text') + @cmdutils.register(instance='status-command', name='set-cmd-text') def set_cmd_text_command(self, text): """Preset the statusbar to some text. @@ -172,7 +172,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): Args: text: The commandline to set. """ - url = utils.get_object('tabbedbrowser').current_url().toString( + url = utils.get_object('tabbed-browser').current_url().toString( QUrl.FullyEncoded | QUrl.RemovePassword) # FIXME we currently replace the URL in any place in the arguments, # rather than just replacing it if it is a dedicated argument. We could @@ -215,7 +215,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): self.setFocus() self.show_cmd.emit() - @cmdutils.register(instance='status-cmd', hide=True, + @cmdutils.register(instance='status-command', hide=True, modes=[usertypes.KeyMode.command]) def command_history_prev(self): """Go back in the commandline history.""" @@ -230,7 +230,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): if item: self.set_cmd_text(item) - @cmdutils.register(instance='status-cmd', hide=True, + @cmdutils.register(instance='status-command', hide=True, modes=[usertypes.KeyMode.command]) def command_history_next(self): """Go forward in the commandline history.""" @@ -243,7 +243,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): if item: self.set_cmd_text(item) - @cmdutils.register(instance='status-cmd', hide=True, + @cmdutils.register(instance='status-command', hide=True, modes=[usertypes.KeyMode.command]) def command_accept(self): """Execute the command currently in the commandline. diff --git a/qutebrowser/widgets/statusbar/prompter.py b/qutebrowser/widgets/statusbar/prompter.py index 9cfbb7cb1..16cacb85c 100644 --- a/qutebrowser/widgets/statusbar/prompter.py +++ b/qutebrowser/widgets/statusbar/prompter.py @@ -280,14 +280,14 @@ class Prompter: self.question = question mode = self._display_question() question.aborted.connect(lambda: modeman.maybe_leave(mode, 'aborted')) - modeman_obj = utils.get_object('modeman') + mode_manager = utils.get_object('mode-manager') try: modeman.enter(mode, 'question asked') except modeman.ModeLockedError: - if modeman_obj.mode() != usertypes.KeyMode.prompt: + if mode_manager.mode() != usertypes.KeyMode.prompt: question.abort() return None - modeman_obj.locked = True + mode_manager.locked = True if blocking: loop = qtutils.EventLoop() self._loops.append(loop) diff --git a/qutebrowser/widgets/tabwidget.py b/qutebrowser/widgets/tabwidget.py index 68a9e9627..e5af752e3 100644 --- a/qutebrowser/widgets/tabwidget.py +++ b/qutebrowser/widgets/tabwidget.py @@ -209,7 +209,7 @@ class TabBar(QTabBar): confwidth = str(config.get('tabs', 'width')) if confwidth.endswith('%'): perc = int(confwidth.rstrip('%')) - width = utils.get_object('mainwindow').width() * perc / 100 + width = utils.get_object('main-window').width() * perc / 100 else: width = int(confwidth) size = QSize(max(minimum_size.width(), width), height) diff --git a/qutebrowser/widgets/webview.py b/qutebrowser/widgets/webview.py index 2f42754d8..270ce5a9e 100644 --- a/qutebrowser/widgets/webview.py +++ b/qutebrowser/widgets/webview.py @@ -358,8 +358,8 @@ class WebView(QWebView): self._set_load_status(LoadStatus.error) if not config.get('input', 'auto-insert-mode'): return - if (utils.get_object('modeman').mode() == usertypes.KeyMode.insert or - not ok): + cur_mode = utils.get_object('mode-manager').mode() + if curmode == usertypes.KeyMode.insert or not ok: return frame = self.page().currentFrame() try: From 9aa65a2341fa0d05c3bc6e8463b178b6b60e6131 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 23:17:36 +0200 Subject: [PATCH 40/79] Fix lint and bugs --- qutebrowser/app.py | 6 ++++-- qutebrowser/browser/hints.py | 5 +++-- qutebrowser/utils/message.py | 2 +- qutebrowser/utils/utilcmds.py | 4 ++-- qutebrowser/widgets/crash.py | 5 ++--- qutebrowser/widgets/mainwindow.py | 10 ++++++---- qutebrowser/widgets/statusbar/bar.py | 1 - qutebrowser/widgets/webview.py | 2 +- 8 files changed, 19 insertions(+), 16 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 8395f54aa..f6dfa7ae7 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -369,7 +369,7 @@ class Application(QApplication): def _connect_signals(self): """Connect all signals to their slots.""" - # pylint: disable=too-many-statements + # pylint: disable=too-many-statements, too-many-locals # syntactic sugar kp = self._keyparsers main_window = self.registry['main-window'] @@ -747,8 +747,10 @@ class Application(QApplication): # event loop, so we can shut down immediately. self._shutdown(status) - def _shutdown(self, status): # noqa, pylint: disable=too-many-branches + def _shutdown(self, status): # noqa """Second stage of shutdown.""" + # pylint: disable=too-many-branches, too-many-statements + # FIXME refactor this log.destroy.debug("Stage 2 of shutting down...") # Remove eventfilter try: diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 351e862e1..0c30fe410 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -270,8 +270,9 @@ class HintManager(QObject): else: display = 'none' rect = elem.geometry() - return self.HINT_CSS.format(left=rect.x(), top=rect.y(), - config=config.instance(), display=display) + return self.HINT_CSS.format( + left=rect.x(), top=rect.y(), config=utils.get_object('config'), + display=display) def _draw_label(self, elem, string): """Draw a hint label over an element. diff --git a/qutebrowser/utils/message.py b/qutebrowser/utils/message.py index 7fec60d4c..31ef8e615 100644 --- a/qutebrowser/utils/message.py +++ b/qutebrowser/utils/message.py @@ -19,7 +19,7 @@ """Message singleton so we don't have to define unneeded signals.""" -from PyQt5.QtCore import pyqtSignal, QCoreApplication, QObject, QTimer +from PyQt5.QtCore import pyqtSignal, QObject, QTimer from qutebrowser.utils import usertypes, log, utils diff --git a/qutebrowser/utils/utilcmds.py b/qutebrowser/utils/utilcmds.py index cf403f5c6..7c507d910 100644 --- a/qutebrowser/utils/utilcmds.py +++ b/qutebrowser/utils/utilcmds.py @@ -27,7 +27,7 @@ from PyQt5.QtCore import QCoreApplication from qutebrowser.utils import usertypes, log, utils from qutebrowser.commands import runners, cmdexc, cmdutils -from qutebrowser.config import config, style +from qutebrowser.config import style _timers = [] @@ -96,7 +96,7 @@ def debug_all_objects(): @cmdutils.register(debug=True) def debug_cache_stats(): """Print LRU cache stats.""" - config_info = config.instance().get.cache_info() + config_info = utils.get_object('config').get.cache_info() style_info = style.get_stylesheet.cache_info() log.misc.debug('config: {}'.format(config_info)) log.misc.debug('style: {}'.format(style_info)) diff --git a/qutebrowser/widgets/crash.py b/qutebrowser/widgets/crash.py index 1a76da689..4bbb738b6 100644 --- a/qutebrowser/widgets/crash.py +++ b/qutebrowser/widgets/crash.py @@ -29,7 +29,6 @@ from PyQt5.QtCore import Qt, QSize from PyQt5.QtWidgets import (QDialog, QLabel, QTextEdit, QPushButton, QVBoxLayout, QHBoxLayout) -from qutebrowser.config import config from qutebrowser.utils import version, log, utils @@ -131,8 +130,8 @@ class _CrashDialog(QDialog): except Exception: self._crash_info.append(("Version info", traceback.format_exc())) try: - self._crash_info.append(("Config", - config.instance().dump_userconfig())) + conf = utils.get_object('config') + self._crash_info.append(("Config", conf.dump_userconfig())) except Exception: self._crash_info.append(("Config", traceback.format_exc())) diff --git a/qutebrowser/widgets/mainwindow.py b/qutebrowser/widgets/mainwindow.py index 4c32c0fe4..723bd7ad8 100644 --- a/qutebrowser/widgets/mainwindow.py +++ b/qutebrowser/widgets/mainwindow.py @@ -95,10 +95,7 @@ class MainWindow(QWidget): # When we're here the statusbar might not even really exist yet, so # resizing will fail. Therefore, we use singleShot QTimers to make sure # we defer this until everything else is initialized. - QTimer.singleShot( - 0, lambda: self._completion.resize_completion.connect( - self.resize_completion)) - QTimer.singleShot(0, self.resize_completion) + QTimer.singleShot(0, self._connect_resize_completion) #self.retranslateUi(MainWindow) #self.tabWidget.setCurrentIndex(0) #QtCore.QMetaObject.connectSlotsByName(MainWindow) @@ -106,6 +103,11 @@ class MainWindow(QWidget): def __repr__(self): return '<{}>'.format(self.__class__.__name__) + def _connect_resize_completion(self): + """Connect the resize_completion signal and resize it once.""" + self._completion.resize_completion.connect(self.resize_completion) + self.resize_completion() + def _set_default_geometry(self): """Set some sensible default geometry.""" self.setGeometry(QRect(50, 50, 800, 600)) diff --git a/qutebrowser/widgets/statusbar/bar.py b/qutebrowser/widgets/statusbar/bar.py index 9d7944e7c..b2c51b1f6 100644 --- a/qutebrowser/widgets/statusbar/bar.py +++ b/qutebrowser/widgets/statusbar/bar.py @@ -25,7 +25,6 @@ import datetime from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty, Qt from PyQt5.QtWidgets import QWidget, QHBoxLayout, QStackedLayout, QSizePolicy -from qutebrowser.keyinput import modeman from qutebrowser.config import config, style from qutebrowser.utils import usertypes, log, utils from qutebrowser.widgets.statusbar import (command, progress, keystring, diff --git a/qutebrowser/widgets/webview.py b/qutebrowser/widgets/webview.py index 270ce5a9e..8ee2dc11c 100644 --- a/qutebrowser/widgets/webview.py +++ b/qutebrowser/widgets/webview.py @@ -359,7 +359,7 @@ class WebView(QWebView): if not config.get('input', 'auto-insert-mode'): return cur_mode = utils.get_object('mode-manager').mode() - if curmode == usertypes.KeyMode.insert or not ok: + if cur_mode == usertypes.KeyMode.insert or not ok: return frame = self.page().currentFrame() try: From 908a69af185e3116b416f835ff34c2855b7545ad Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Tue, 23 Sep 2014 23:31:17 +0200 Subject: [PATCH 41/79] Better __repr__s --- qutebrowser/app.py | 3 +++ qutebrowser/browser/cache.py | 2 +- qutebrowser/browser/commands.py | 3 +++ qutebrowser/config/iniparsers.py | 6 ++++++ qutebrowser/config/keyconfparser.py | 5 +++++ qutebrowser/config/lineparser.py | 7 +++++++ qutebrowser/utils/completer.py | 3 +++ qutebrowser/utils/readline.py | 3 +++ qutebrowser/widgets/console.py | 7 +++++++ 9 files changed, 38 insertions(+), 1 deletion(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index f6dfa7ae7..f20fcb148 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -156,6 +156,9 @@ class Application(QApplication): if self._crashdlg is not None: self._crashdlg.raise_() + def __repr__(self): + return '<{}>'.format(self.__class__.__name__) + def _init_config(self): """Inizialize and read the config.""" if self._args.confdir is None: diff --git a/qutebrowser/browser/cache.py b/qutebrowser/browser/cache.py index 42d379e75..e9362cc2a 100644 --- a/qutebrowser/browser/cache.py +++ b/qutebrowser/browser/cache.py @@ -39,6 +39,6 @@ class DiskCache(QNetworkDiskCache): self.setMaximumCacheSize(config.get('storage', 'cache-size')) def __repr__(self): - return '<{} size={}, maxsize={}, path={}>'.format( + return '<{} size={}, maxsize={}, path="{}">'.format( self.__class__.__name__, self.cacheSize(), self.maximumCacheSize(), self.cacheDirectory()) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index f3bbf0a1c..4af4cfef2 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -65,6 +65,9 @@ class CommandDispatcher: self._tabs = parent self._editor = None + def __repr__(self): + return '<{}>'.format(self.__class__.__name__) + def _current_widget(self): """Get the currently active widget from a command.""" widget = self._tabs.currentWidget() diff --git a/qutebrowser/config/iniparsers.py b/qutebrowser/config/iniparsers.py index 7e238320e..e9e2cdb87 100644 --- a/qutebrowser/config/iniparsers.py +++ b/qutebrowser/config/iniparsers.py @@ -32,6 +32,7 @@ class ReadConfigParser(configparser.ConfigParser): Attributes: _configdir: The directory to read the config from. + _fname: The filename of the config. _configfile: The config file path. """ @@ -45,12 +46,17 @@ class ReadConfigParser(configparser.ConfigParser): super().__init__(interpolation=None, comment_prefixes='#') self.optionxform = lambda opt: opt # be case-insensitive self._configdir = configdir + self._fname = fname self._configfile = os.path.join(self._configdir, fname) if not os.path.isfile(self._configfile): return log.init.debug("Reading config from {}".format(self._configfile)) self.read(self._configfile, encoding='utf-8') + def __repr__(self): + return '{}("{}", "{}")'.format( + self.__class__.__name__, self._configdir, self._fname) + class ReadWriteConfigParser(ReadConfigParser): diff --git a/qutebrowser/config/keyconfparser.py b/qutebrowser/config/keyconfparser.py index 8545c7764..03e8f73bd 100644 --- a/qutebrowser/config/keyconfparser.py +++ b/qutebrowser/config/keyconfparser.py @@ -65,6 +65,7 @@ class KeyConfigParser(QObject): """ super().__init__(parent) self._configdir = configdir + self._fname = fname self._configfile = os.path.join(self._configdir, fname) self._cur_section = None self._cur_command = None @@ -97,6 +98,10 @@ class KeyConfigParser(QObject): lines.append('') return '\n'.join(lines) + '\n' + def __repr__(self): + return '{}("{}", "{}")'.format( + self.__class__.__name__, self._configdir, self._fname) + def _str_section_desc(self, sectname): """Get the section description string for sectname.""" wrapper = textwrapper.TextWrapper() diff --git a/qutebrowser/config/lineparser.py b/qutebrowser/config/lineparser.py index c980ec303..18e5b61a4 100644 --- a/qutebrowser/config/lineparser.py +++ b/qutebrowser/config/lineparser.py @@ -35,6 +35,7 @@ class LineConfigParser: data: A list of lines. _configdir: The directory to read the config from. _configfile: The config file path. + _fname: Filename of the config. _binary: Whether to open the file in binary mode. """ @@ -49,6 +50,7 @@ class LineConfigParser: """ self._configdir = configdir self._configfile = os.path.join(self._configdir, fname) + self._fname = fname self._limit = limit self._binary = binary if not os.path.isfile(self._configfile): @@ -57,6 +59,11 @@ class LineConfigParser: log.init.debug("Reading config from {}".format(self._configfile)) self.read(self._configfile) + def __repr__(self): + return '{}("{}", "{}", limit={}, binary={})'.format( + self.__class__.__name__, self._configdir, self._fname, self._limit, + self._binary) + def __iter__(self): """Iterate over the set data.""" return self.data.__iter__() diff --git a/qutebrowser/utils/completer.py b/qutebrowser/utils/completer.py index 22bf59577..b951f66f3 100644 --- a/qutebrowser/utils/completer.py +++ b/qutebrowser/utils/completer.py @@ -60,6 +60,9 @@ class Completer(QObject): self._init_static_completions() self._init_setting_completions() + def __repr__(self): + return '<{}>'.format(self.__class__.__name__) + def _init_static_completions(self): """Initialize the static completion models.""" self._models[usertypes.Completion.command] = CFM( diff --git a/qutebrowser/utils/readline.py b/qutebrowser/utils/readline.py index e5e13672d..d398e2f7c 100644 --- a/qutebrowser/utils/readline.py +++ b/qutebrowser/utils/readline.py @@ -36,6 +36,9 @@ class ReadlineBridge: def __init__(self): self.deleted = {} + def __repr__(self): + return '<{}>'.format(self.__class__.__name__) + def _widget(self): """Get the currently active QLineEdit.""" w = QApplication.instance().focusWidget() diff --git a/qutebrowser/widgets/console.py b/qutebrowser/widgets/console.py index 8ae2904ff..fdd4c395b 100644 --- a/qutebrowser/widgets/console.py +++ b/qutebrowser/widgets/console.py @@ -151,6 +151,9 @@ class ConsoleTextEdit(QTextEdit): self.setFont(config.get('fonts', 'debug-console')) self.setFocusPolicy(Qt.NoFocus) + def __repr__(self): + return '<{}>'.format(self.__class__.__name__) + def on_config_changed(self, section, option): """Update font when config changed.""" if section == 'fonts' and option == 'debug-console': @@ -173,6 +176,10 @@ class ConsoleWidget(QWidget): self.setLayout(self.vbox) self.lineedit.setFocus() + def __repr__(self): + return '<{}, visible={}>'.format( + self.__class__.__name__, self.isVisible()) + @pyqtSlot(str, str) def on_config_changed(self, section, option): """Update font when config changed.""" From a2d3ca6565c3b2ef60a274313446616f91fa06bc Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 06:25:08 +0200 Subject: [PATCH 42/79] Make it possible to update an object in the object registry. --- qutebrowser/utils/usertypes.py | 6 +----- qutebrowser/utils/utils.py | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index 51ee611d2..81af0a357 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -395,12 +395,8 @@ class ObjectRegistry(collections.UserDict): def __setitem__(self, name, obj): """Register an object in the object registry. - Prevents duplicated registrations and sets a slot to remove QObjects - when they are destroyed. + Sets a slot to remove QObjects when they are destroyed. """ - if name in self.data: - raise KeyError("Object '{}' is already registered ({})!".format( - name, repr(self.data[name]))) if isinstance(obj, QObject): obj.destroyed.connect(functools.partial(self.on_destroyed, name)) super().__setitem__(name, obj) diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index 912d8f952..4a6b4177e 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -585,6 +585,18 @@ def get_object(name): return QCoreApplication.instance().registry[name] -def register_object(name, obj): - """Helper function to register an object.""" - QCoreApplication.instance().registry[name] = obj +def register_object(name, obj, update=False): + """Helper function to register an object. + + Args: + name: The name the object will be registered as. + obj: The object to register. + update: If True, allows to update an already registered object. + """ + registry = QCoreApplication.instance().registry + if not update and name in registry: + raise KeyError("Object '{}' is already registered ({})!".format( + name, repr(registry[name]))) + registry[name] = obj + + From d32d6c9b285e20b87b5263145e51f52018a1815f Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 06:41:22 +0200 Subject: [PATCH 43/79] Allow a default value for utils.get_object. --- qutebrowser/utils/utils.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index 4a6b4177e..804060591 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -39,6 +39,9 @@ import qutebrowser from qutebrowser.utils import qtutils, log +_UNSET = object() + + def elide(text, length): """Elide text so it uses a maximum of length chars.""" if length < 1: @@ -580,9 +583,19 @@ def is_enum(obj): return False -def get_object(name): - """Helper function to get an object.""" - return QCoreApplication.instance().registry[name] +def get_object(name, default=_UNSET): + """Helper function to get an object. + + Args: + default: A default to return if the object does not exist. + """ + try: + return QCoreApplication.instance().registry[name] + except KeyError: + if default is not _UNSET: + return default + else: + raise def register_object(name, obj, update=False): From 6090bf418df85acef5871abb4951a2eb84f9344b Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 06:41:39 +0200 Subject: [PATCH 44/79] Add utils.delete_object() --- qutebrowser/utils/utils.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index 804060591..b8e306ff9 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -613,3 +613,6 @@ def register_object(name, obj, update=False): registry[name] = obj +def delete_object(name): + """Helper function to unregister an object.""" + del QCoreApplication.instance().registry[name] From b121ceef216adf0b95e78e681fb4fc97de961519 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 06:41:51 +0200 Subject: [PATCH 45/79] Use object registry for last focused tab. --- qutebrowser/browser/commands.py | 9 +++++---- qutebrowser/widgets/tabbedbrowser.py | 12 ++++++------ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 4af4cfef2..d8f64f5f5 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -38,7 +38,7 @@ from qutebrowser.commands import userscripts, cmdexc, cmdutils from qutebrowser.config import config from qutebrowser.browser import hints, quickmarks, webelem from qutebrowser.utils import (message, editor, usertypes, log, qtutils, - urlutils) + urlutils, utils) class CommandDispatcher: @@ -52,7 +52,6 @@ class CommandDispatcher: cmdutils.register() decorators are run, currentWidget() will return None. Attributes: - _tabs: The TabbedBrowser object. _editor: The ExternalEditor object. """ @@ -143,9 +142,11 @@ class CommandDispatcher: def _tab_focus_last(self): """Select the tab which was last focused.""" - if self._tabs.last_focused is None: + try: + tab = utils.get_object('last-focused-tab') + except KeyError: raise cmdexc.CommandError("No last focused tab!") - idx = self._tabs.indexOf(self._tabs.last_focused) + idx = self._tabs.indexOf(tab) if idx == -1: raise cmdexc.CommandError("Last focused tab vanished!") self._tabs.setCurrentIndex(idx) diff --git a/qutebrowser/widgets/tabbedbrowser.py b/qutebrowser/widgets/tabbedbrowser.py index eae530e2b..82a5f5d51 100644 --- a/qutebrowser/widgets/tabbedbrowser.py +++ b/qutebrowser/widgets/tabbedbrowser.py @@ -54,7 +54,6 @@ class TabbedBrowser(tabwidget.TabWidget): tabbar -> new-tab-position set to 'left'. _tab_insert_idx_right: Same as above, for 'right'. url_stack: Stack of URLs of closed tabs. - last_focused: The tab which was focused last. Signals: cur_progress: Progress of the current tab changed (loadProgress). @@ -111,7 +110,6 @@ class TabbedBrowser(tabwidget.TabWidget): self._filter = signalfilter.SignalFilter(self) dispatcher = commands.CommandDispatcher(self) utils.register_object('command-dispatcher', dispatcher) - self.last_focused = None self._now_focused = None # FIXME adjust this to font size self.setIconSize(QSize(12, 12)) @@ -263,8 +261,8 @@ class TabbedBrowser(tabwidget.TabWidget): tab)) if tab is self._now_focused: self._now_focused = None - if tab is self.last_focused: - self.last_focused = None + if tab is utils.get_object('last-focused-tab', None): + utils.delete_object('last-focused-tab') if not tab.cur_url.isEmpty(): qtutils.ensure_valid(tab.cur_url) self.url_stack.append(tab.cur_url) @@ -522,11 +520,13 @@ class TabbedBrowser(tabwidget.TabWidget): @pyqtSlot(int) def on_current_changed(self, idx): - """Set last_focused and leave hinting mode when focus changed.""" + """Set last-focused-tab and leave hinting mode when focus changed.""" tab = self.widget(idx) tab.setFocus() modeman.maybe_leave(usertypes.KeyMode.hint, 'tab changed') - self.last_focused = self._now_focused + if self._now_focused is not None: + utils.register_object('last-focused-tab', self._now_focused, + update=True) self._now_focused = tab self.current_tab_changed.emit(tab) self.title_changed.emit('{} - qutebrowser'.format(self.tabText(idx))) From 3f024518288f9122cc788078da2fb80a352ff315 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 07:06:45 +0200 Subject: [PATCH 46/79] Move object registry to its own file. --- qutebrowser/app.py | 8 +- qutebrowser/browser/commands.py | 4 +- qutebrowser/browser/downloads.py | 5 +- qutebrowser/browser/hints.py | 12 +-- qutebrowser/commands/argparser.py | 4 +- qutebrowser/commands/command.py | 7 +- qutebrowser/commands/runners.py | 4 +- qutebrowser/config/config.py | 6 +- qutebrowser/config/style.py | 8 +- qutebrowser/keyinput/basekeyparser.py | 4 +- qutebrowser/keyinput/modeman.py | 12 +-- qutebrowser/models/completion.py | 6 +- qutebrowser/models/downloadmodel.py | 8 +- qutebrowser/network/networkmanager.py | 6 +- qutebrowser/utils/completer.py | 4 +- qutebrowser/utils/message.py | 16 ++-- qutebrowser/utils/objreg.py | 95 +++++++++++++++++++++++ qutebrowser/utils/usertypes.py | 33 -------- qutebrowser/utils/utilcmds.py | 6 +- qutebrowser/utils/utils.py | 38 --------- qutebrowser/widgets/completion.py | 6 +- qutebrowser/widgets/crash.py | 4 +- qutebrowser/widgets/mainwindow.py | 8 +- qutebrowser/widgets/statusbar/bar.py | 8 +- qutebrowser/widgets/statusbar/command.py | 6 +- qutebrowser/widgets/statusbar/prompt.py | 4 +- qutebrowser/widgets/statusbar/prompter.py | 4 +- qutebrowser/widgets/tabbedbrowser.py | 11 ++- qutebrowser/widgets/tabwidget.py | 4 +- qutebrowser/widgets/webview.py | 4 +- 30 files changed, 185 insertions(+), 160 deletions(-) create mode 100644 qutebrowser/utils/objreg.py diff --git a/qutebrowser/app.py b/qutebrowser/app.py index f20fcb148..3688d202c 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -44,7 +44,7 @@ from qutebrowser.browser import quickmarks, cookies, downloads, cache from qutebrowser.widgets import mainwindow, console, crash from qutebrowser.keyinput import modeparsers, keyparser, modeman from qutebrowser.utils import (log, version, message, utilcmds, readline, - utils, qtutils, urlutils, debug) + utils, qtutils, urlutils, debug, objreg) from qutebrowser.utils import usertypes as utypes @@ -83,8 +83,8 @@ class Application(QApplication): 'tabs': False, 'main': False, } - self.meta_registry = utypes.ObjectRegistry() - self.registry = utypes.ObjectRegistry() + self.meta_registry = objreg.ObjectRegistry() + self.registry = objreg.ObjectRegistry() self.meta_registry['global'] = self.registry self.registry['app'] = self self._shutting_down = False @@ -626,7 +626,7 @@ class Application(QApplication): # exceptions occur. if pages is None: pages = [] - for tab in utils.get_object('tabbed-browser').widgets(): + for tab in objreg.get('tabbed-browser').widgets(): urlstr = tab.cur_url.toString( QUrl.RemovePassword | QUrl.FullyEncoded) if urlstr: diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index d8f64f5f5..8e3351148 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -38,7 +38,7 @@ from qutebrowser.commands import userscripts, cmdexc, cmdutils from qutebrowser.config import config from qutebrowser.browser import hints, quickmarks, webelem from qutebrowser.utils import (message, editor, usertypes, log, qtutils, - urlutils, utils) + urlutils, objreg) class CommandDispatcher: @@ -143,7 +143,7 @@ class CommandDispatcher: def _tab_focus_last(self): """Select the tab which was last focused.""" try: - tab = utils.get_object('last-focused-tab') + tab = objreg.get('last-focused-tab') except KeyError: raise cmdexc.CommandError("No last focused tab!") idx = self._tabs.indexOf(tab) diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py index bc9f21339..ea80c85bb 100644 --- a/qutebrowser/browser/downloads.py +++ b/qutebrowser/browser/downloads.py @@ -29,7 +29,8 @@ from PyQt5.QtNetwork import QNetworkRequest, QNetworkReply from qutebrowser.config import config from qutebrowser.commands import cmdexc, cmdutils -from qutebrowser.utils import message, http, usertypes, log, utils, qtutils +from qutebrowser.utils import (message, http, usertypes, log, utils, qtutils, + objreg) class DownloadItem(QObject): @@ -409,7 +410,7 @@ class DownloadManager(QObject): q.destroyed.connect(functools.partial(self.questions.remove, q)) self.questions.append(q) download.cancelled.connect(q.abort) - utils.get_object('message-bridge').ask(q, blocking=False) + objreg.get('message-bridge').ask(q, blocking=False) @pyqtSlot(DownloadItem) def on_finished(self, download): diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 0c30fe410..90ffddf2f 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -31,7 +31,7 @@ from qutebrowser.config import config from qutebrowser.keyinput import modeman from qutebrowser.browser import webelem from qutebrowser.commands import userscripts, cmdexc -from qutebrowser.utils import usertypes, log, qtutils, message, utils +from qutebrowser.utils import usertypes, log, qtutils, message, objreg ElemTuple = collections.namedtuple('ElemTuple', 'elem, label') @@ -149,8 +149,8 @@ class HintManager(QObject): """ super().__init__(parent) self._context = None - utils.get_object('mode-manager').left.connect(self.on_mode_left) - utils.get_object('mode-manager').entered.connect(self.on_mode_entered) + objreg.get('mode-manager').left.connect(self.on_mode_left) + objreg.get('mode-manager').entered.connect(self.on_mode_entered) def _cleanup(self): """Clean up after hinting.""" @@ -160,7 +160,7 @@ class HintManager(QObject): except webelem.IsNullError: pass text = self.HINT_TEXTS[self._context.target] - utils.get_object('message-bridge').maybe_reset_text(text) + objreg.get('message-bridge').maybe_reset_text(text) self._context = None def _hint_strings(self, elems): @@ -271,7 +271,7 @@ class HintManager(QObject): display = 'none' rect = elem.geometry() return self.HINT_CSS.format( - left=rect.x(), top=rect.y(), config=utils.get_object('config'), + left=rect.x(), top=rect.y(), config=objreg.get('config'), display=display) def _draw_label(self, elem, string): @@ -542,7 +542,7 @@ class HintManager(QObject): self._context.frames = webelem.get_child_frames(mainframe) self._context.args = args self._init_elements(mainframe, group) - utils.get_object('message-bridge').set_text(self.HINT_TEXTS[target]) + objreg.get('message-bridge').set_text(self.HINT_TEXTS[target]) self._connect_frame_signals() try: modeman.enter(usertypes.KeyMode.hint, 'HintManager.start') diff --git a/qutebrowser/commands/argparser.py b/qutebrowser/commands/argparser.py index a92a1599a..9ef3d59aa 100644 --- a/qutebrowser/commands/argparser.py +++ b/qutebrowser/commands/argparser.py @@ -25,7 +25,7 @@ import argparse from PyQt5.QtCore import QUrl from qutebrowser.commands import cmdexc -from qutebrowser.utils import utils +from qutebrowser.utils import utils, objreg SUPPRESS = argparse.SUPPRESS @@ -54,7 +54,7 @@ class HelpAction(argparse.Action): """ def __call__(self, parser, _namespace, _values, _option_string=None): - utils.get_object('tabbed-browser').tabopen( + objreg.get('tabbed-browser').tabopen( QUrl('qute://help/commands.html#{}'.format(parser.name))) parser.exit() diff --git a/qutebrowser/commands/command.py b/qutebrowser/commands/command.py index 3e65d69bc..b2dd39810 100644 --- a/qutebrowser/commands/command.py +++ b/qutebrowser/commands/command.py @@ -25,7 +25,8 @@ import collections from PyQt5.QtWebKit import QWebSettings from qutebrowser.commands import cmdexc, argparser -from qutebrowser.utils import log, utils, message, debug, usertypes, docutils +from qutebrowser.utils import (log, utils, message, debug, usertypes, docutils, + objreg) class Command: @@ -97,7 +98,7 @@ class Command: Raise: PrerequisitesError if the command can't be called currently. """ - curmode = utils.get_object('mode-manager').mode() + curmode = objreg.get('mode-manager').mode() if self.modes is not None and curmode not in self.modes: mode_names = '/'.join(mode.name for mode in self.modes) raise cmdexc.PrerequisitesError( @@ -298,7 +299,7 @@ class Command: args: The positional argument list. Gets modified directly. """ assert param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD - obj = utils.get_object(self.instance) + obj = objreg.get(self.instance) args.append(obj) def _get_count_arg(self, param, args, kwargs): diff --git a/qutebrowser/commands/runners.py b/qutebrowser/commands/runners.py index 2b934222f..9821c9706 100644 --- a/qutebrowser/commands/runners.py +++ b/qutebrowser/commands/runners.py @@ -24,7 +24,7 @@ from PyQt5.QtWebKitWidgets import QWebPage from qutebrowser.config import config from qutebrowser.commands import cmdexc, cmdutils -from qutebrowser.utils import message, log, utils +from qutebrowser.utils import message, log, utils, objreg def replace_variables(arglist): @@ -32,7 +32,7 @@ def replace_variables(arglist): args = [] for arg in arglist: if arg == '{url}': - url = utils.get_object('tabbed-browser').current_url().toString( + url = objreg.get('tabbed-browser').current_url().toString( QUrl.FullyEncoded | QUrl.RemovePassword) args.append(url) else: diff --git a/qutebrowser/config/config.py b/qutebrowser/config/config.py index 5e28d5997..f76ab7415 100644 --- a/qutebrowser/config/config.py +++ b/qutebrowser/config/config.py @@ -35,18 +35,18 @@ from PyQt5.QtCore import pyqtSignal, QObject from qutebrowser.utils import log from qutebrowser.config import configdata, iniparsers, configtypes, textwrapper from qutebrowser.commands import cmdexc, cmdutils -from qutebrowser.utils import message, utils +from qutebrowser.utils import message, objreg from qutebrowser.utils.usertypes import Completion def get(*args, **kwargs): """Convenience method to call get(...) of the config instance.""" - return utils.get_object('config').get(*args, **kwargs) + return objreg.get('config').get(*args, **kwargs) def section(sect): """Get a config section from the global config.""" - return utils.get_object('config')[sect] + return objreg.get('config')[sect] class NoSectionError(configparser.NoSectionError): diff --git a/qutebrowser/config/style.py b/qutebrowser/config/style.py index 12c21c739..4b964c87c 100644 --- a/qutebrowser/config/style.py +++ b/qutebrowser/config/style.py @@ -25,7 +25,7 @@ import jinja2 from PyQt5.QtGui import QColor from qutebrowser.config import config -from qutebrowser.utils import log, utils +from qutebrowser.utils import log, objreg @functools.lru_cache(maxsize=16) @@ -42,7 +42,7 @@ def get_stylesheet(template_str): fontdict = FontDict(config.section('fonts')) template = jinja2.Template(template_str) return template.render(color=colordict, font=fontdict, - config=utils.get_object('config')) + config=objreg.get('config')) def set_register_stylesheet(obj): @@ -60,8 +60,8 @@ def set_register_stylesheet(obj): log.style.vdebug("stylesheet for {}: {}".format( obj.__class__.__name__, qss)) obj.setStyleSheet(qss) - utils.get_object('config').changed.connect( - functools.partial(_update_stylesheet, obj)) + objreg.get('config').changed.connect(functools.partial( + _update_stylesheet, obj)) def _update_stylesheet(obj, _section, _option): diff --git a/qutebrowser/keyinput/basekeyparser.py b/qutebrowser/keyinput/basekeyparser.py index ebfb51d81..e5be993f8 100644 --- a/qutebrowser/keyinput/basekeyparser.py +++ b/qutebrowser/keyinput/basekeyparser.py @@ -26,7 +26,7 @@ import functools from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QObject from qutebrowser.config import config -from qutebrowser.utils import usertypes, log, utils +from qutebrowser.utils import usertypes, log, utils, objreg class BaseKeyParser(QObject): @@ -321,7 +321,7 @@ class BaseKeyParser(QObject): self._modename = modename self.bindings = {} self.special_bindings = {} - keyconfparser = utils.get_object('key-config') + keyconfparser = objreg.get('key-config') for (key, cmd) in keyconfparser.get_bindings_for(modename).items(): if not cmd: continue diff --git a/qutebrowser/keyinput/modeman.py b/qutebrowser/keyinput/modeman.py index 22a561d8b..41fa44aae 100644 --- a/qutebrowser/keyinput/modeman.py +++ b/qutebrowser/keyinput/modeman.py @@ -29,7 +29,7 @@ from PyQt5.QtWidgets import QApplication from qutebrowser.config import config from qutebrowser.commands import cmdexc, cmdutils -from qutebrowser.utils import usertypes, log, utils +from qutebrowser.utils import usertypes, log, objreg class ModeLockedError(Exception): @@ -39,18 +39,18 @@ class ModeLockedError(Exception): def enter(mode, reason=None): """Enter the mode 'mode'.""" - utils.get_object('mode-manager').enter(mode, reason) + objreg.get('mode-manager').enter(mode, reason) def leave(mode, reason=None): """Leave the mode 'mode'.""" - utils.get_object('mode-manager').leave(mode, reason) + objreg.get('mode-manager').leave(mode, reason) def maybe_enter(mode, reason=None): """Convenience method to enter 'mode' without exceptions.""" try: - utils.get_object('mode-manager').enter(mode, reason) + objreg.get('mode-manager').enter(mode, reason) except ModeLockedError: pass @@ -58,7 +58,7 @@ def maybe_enter(mode, reason=None): def maybe_leave(mode, reason=None): """Convenience method to leave 'mode' without exceptions.""" try: - utils.get_object('mode-manager').leave(mode, reason) + objreg.get('mode-manager').leave(mode, reason) except ValueError as e: # This is rather likely to happen, so we only log to debug log. log.modes.debug(e) @@ -283,7 +283,7 @@ class ModeManager(QObject): # we're not interested in it anymore. return False if (QApplication.instance().activeWindow() is not - utils.get_object('main-window')): + objreg.get('main-window')): # Some other window (print dialog, etc.) is focused so we pass # the event through. return False diff --git a/qutebrowser/models/completion.py b/qutebrowser/models/completion.py index 3b2c66dc1..d15096d7c 100644 --- a/qutebrowser/models/completion.py +++ b/qutebrowser/models/completion.py @@ -23,7 +23,7 @@ from PyQt5.QtCore import pyqtSlot, Qt from qutebrowser.config import config, configdata from qutebrowser.models import basecompletion -from qutebrowser.utils import log, qtutils, utils +from qutebrowser.utils import log, qtutils, objreg from qutebrowser.commands import cmdutils @@ -155,7 +155,7 @@ class CommandCompletionModel(basecompletion.BaseCompletionModel): assert cmdutils.cmd_dict cmdlist = [] for obj in set(cmdutils.cmd_dict.values()): - if obj.hide or (obj.debug and not utils.get_object('args').debug): + if obj.hide or (obj.debug and not objreg.get('args').debug): pass else: cmdlist.append((obj.name, obj.desc)) @@ -182,7 +182,7 @@ class HelpCompletionModel(basecompletion.BaseCompletionModel): assert cmdutils.cmd_dict cmdlist = [] for obj in set(cmdutils.cmd_dict.values()): - if obj.hide or (obj.debug and not utils.get_object('args').debug): + if obj.hide or (obj.debug and not objreg.get('args').debug): pass else: cmdlist.append((':' + obj.name, obj.desc)) diff --git a/qutebrowser/models/downloadmodel.py b/qutebrowser/models/downloadmodel.py index a99bb8188..4f0a15954 100644 --- a/qutebrowser/models/downloadmodel.py +++ b/qutebrowser/models/downloadmodel.py @@ -23,7 +23,7 @@ from PyQt5.QtCore import (pyqtSlot, Qt, QVariant, QAbstractListModel, QModelIndex) from qutebrowser.config import config -from qutebrowser.utils import usertypes, qtutils, utils +from qutebrowser.utils import usertypes, qtutils, objreg Role = usertypes.enum('Role', 'item', start=Qt.UserRole, is_int=True) @@ -39,7 +39,7 @@ class DownloadModel(QAbstractListModel): def __init__(self, parent=None): super().__init__(parent) - download_manager = utils.get_object('download-manager') + download_manager = objreg.get('download-manager') download_manager.download_about_to_be_added.connect( lambda idx: self.beginInsertRows(QModelIndex(), idx, idx)) download_manager.download_added.connect(self.endInsertRows) @@ -81,7 +81,7 @@ class DownloadModel(QAbstractListModel): if index.parent().isValid() or index.column() != 0: return QVariant() - item = utils.get_object('download-manager').downloads[index.row()] + item = objreg.get('download-manager').downloads[index.row()] if role == Qt.DisplayRole: data = str(item) elif role == Qt.ForegroundRole: @@ -105,4 +105,4 @@ class DownloadModel(QAbstractListModel): if parent.isValid(): # We don't have children return 0 - return len(utils.get_object('download-manager').downloads) + return len(objreg.get('download-manager').downloads) diff --git a/qutebrowser/network/networkmanager.py b/qutebrowser/network/networkmanager.py index 7bcf6d665..e306af54d 100644 --- a/qutebrowser/network/networkmanager.py +++ b/qutebrowser/network/networkmanager.py @@ -30,7 +30,7 @@ else: SSL_AVAILABLE = QSslSocket.supportsSsl() from qutebrowser.config import config -from qutebrowser.utils import message, log, usertypes, utils +from qutebrowser.utils import message, log, usertypes, utils, objreg from qutebrowser.network import qutescheme, schemehandler @@ -55,10 +55,10 @@ class NetworkManager(QNetworkAccessManager): # We have a shared cookie jar and cache - we restore their parents so # we don't take ownership of them. app = QCoreApplication.instance() - cookie_jar = utils.get_object('cookie-jar') + cookie_jar = objreg.get('cookie-jar') self.setCookieJar(cookie_jar) cookie_jar.setParent(app) - cache = utils.get_object('cache') + cache = objreg.get('cache') self.setCache(cache) cache.setParent(app) diff --git a/qutebrowser/utils/completer.py b/qutebrowser/utils/completer.py index b951f66f3..748d3d38d 100644 --- a/qutebrowser/utils/completer.py +++ b/qutebrowser/utils/completer.py @@ -23,7 +23,7 @@ from PyQt5.QtCore import pyqtSlot, pyqtSignal, QObject from qutebrowser.config import config, configdata from qutebrowser.commands import cmdutils -from qutebrowser.utils import usertypes, log, utils +from qutebrowser.utils import usertypes, log, objreg from qutebrowser.models import completion as models from qutebrowser.models.completionfilter import CompletionFilterModel as CFM @@ -72,7 +72,7 @@ class Completer(QObject): def _init_setting_completions(self): """Initialize setting completion models.""" - config_obj = utils.get_object('config') + config_obj = objreg.get('config') self._models[usertypes.Completion.section] = CFM( models.SettingSectionCompletionModel(self), self) self._models[usertypes.Completion.option] = {} diff --git a/qutebrowser/utils/message.py b/qutebrowser/utils/message.py index 31ef8e615..7d11c84d3 100644 --- a/qutebrowser/utils/message.py +++ b/qutebrowser/utils/message.py @@ -21,7 +21,7 @@ from PyQt5.QtCore import pyqtSignal, QObject, QTimer -from qutebrowser.utils import usertypes, log, utils +from qutebrowser.utils import usertypes, log, objreg def error(message, immediately=False): @@ -30,7 +30,7 @@ def error(message, immediately=False): Args: See MessageBridge.error. """ - utils.get_object('message-bridge').error(message, immediately) + objreg.get('message-bridge').error(message, immediately) def info(message, immediately=True): @@ -39,12 +39,12 @@ def info(message, immediately=True): Args: See MessageBridge.info. """ - utils.get_object('message-bridge').info(message, immediately) + objreg.get('message-bridge').info(message, immediately) def set_cmd_text(txt): """Convienience function to Set the statusbar command line to a text.""" - utils.get_object('message-bridge').set_cmd_text(txt) + objreg.get('message-bridge').set_cmd_text(txt) def ask(message, mode, default=None): @@ -62,7 +62,7 @@ def ask(message, mode, default=None): q.text = message q.mode = mode q.default = default - utils.get_object('message-bridge').ask(q, blocking=True) + objreg.get('message-bridge').ask(q, blocking=True) q.deleteLater() return q.answer @@ -72,7 +72,7 @@ def alert(message): q = usertypes.Question() q.text = message q.mode = usertypes.PromptMode.alert - utils.get_object('message-bridge').ask(q, blocking=True) + objreg.get('message-bridge').ask(q, blocking=True) q.deleteLater() @@ -87,7 +87,7 @@ def ask_async(message, mode, handler, default=None): """ if not isinstance(mode, usertypes.PromptMode): raise TypeError("Mode {} is no PromptMode member!".format(mode)) - bridge = utils.get_object('message-bridge') + bridge = objreg.get('message-bridge') q = usertypes.Question(bridge) q.text = message q.mode = mode @@ -106,7 +106,7 @@ def confirm_async(message, yes_action, no_action=None, default=None): no_action: Callable to be called when the user answered no. default: True/False to set a default value, or None. """ - bridge = utils.get_object('message-bridge') + bridge = objreg.get('message-bridge') q = usertypes.Question(bridge) q.text = message q.mode = usertypes.PromptMode.yesno diff --git a/qutebrowser/utils/objreg.py b/qutebrowser/utils/objreg.py new file mode 100644 index 000000000..d83727c9a --- /dev/null +++ b/qutebrowser/utils/objreg.py @@ -0,0 +1,95 @@ +# vim: ft=python fileencoding=utf-8 sts=4 sw=4 et: + +# Copyright 2014 Florian Bruhin (The Compiler) +# +# This file is part of qutebrowser. +# +# qutebrowser is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# qutebrowser is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with qutebrowser. If not, see . + +"""The global object registry and related utility functions.""" + + +import collections +import functools + +from PyQt5.QtCore import QCoreApplication, QObject + + +_UNSET = object() + + +class ObjectRegistry(collections.UserDict): + + """A registry of long-living objects in qutebrowser. + + Inspired by the eric IDE code (E5Gui/E5Application.py). + """ + + def __setitem__(self, name, obj): + """Register an object in the object registry. + + Sets a slot to remove QObjects when they are destroyed. + """ + if isinstance(obj, QObject): + obj.destroyed.connect(functools.partial(self.on_destroyed, name)) + super().__setitem__(name, obj) + + def on_destroyed(self, name): + """Remove a destroyed QObject.""" + try: + del self[name] + except KeyError: + pass + + def dump_objects(self): + """Dump all objects as a list of strings.""" + lines = [] + for name, obj in self.data.items(): + lines.append("{}: {}".format(name, repr(obj))) + return lines + + +def get(name, default=_UNSET): + """Helper function to get an object. + + Args: + default: A default to return if the object does not exist. + """ + try: + return QCoreApplication.instance().registry[name] + except KeyError: + if default is not _UNSET: + return default + else: + raise + + +def register(name, obj, update=False): + """Helper function to register an object. + + Args: + name: The name the object will be registered as. + obj: The object to register. + update: If True, allows to update an already registered object. + """ + registry = QCoreApplication.instance().registry + if not update and name in registry: + raise KeyError("Object '{}' is already registered ({})!".format( + name, repr(registry[name]))) + registry[name] = obj + + +def delete(name): + """Helper function to unregister an object.""" + del QCoreApplication.instance().registry[name] diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index 81af0a357..e17e231ac 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -24,8 +24,6 @@ Module attributes: """ import operator -import functools -import collections import collections.abc import enum as pyenum @@ -383,34 +381,3 @@ class Timer(QTimer): super().start(msec) else: super().start() - - -class ObjectRegistry(collections.UserDict): - - """A registry of long-living objects in qutebrowser. - - Inspired by the eric IDE code (E5Gui/E5Application.py). - """ - - def __setitem__(self, name, obj): - """Register an object in the object registry. - - Sets a slot to remove QObjects when they are destroyed. - """ - if isinstance(obj, QObject): - obj.destroyed.connect(functools.partial(self.on_destroyed, name)) - super().__setitem__(name, obj) - - def on_destroyed(self, name): - """Remove a destroyed QObject.""" - try: - del self[name] - except KeyError: - pass - - def dump_objects(self): - """Dump all objects as a list of strings.""" - lines = [] - for name, obj in self.data.items(): - lines.append("{}: {}".format(name, repr(obj))) - return lines diff --git a/qutebrowser/utils/utilcmds.py b/qutebrowser/utils/utilcmds.py index 7c507d910..82d2a8e09 100644 --- a/qutebrowser/utils/utilcmds.py +++ b/qutebrowser/utils/utilcmds.py @@ -25,7 +25,7 @@ import functools from PyQt5.QtCore import QCoreApplication -from qutebrowser.utils import usertypes, log, utils +from qutebrowser.utils import usertypes, log, objreg from qutebrowser.commands import runners, cmdexc, cmdutils from qutebrowser.config import style @@ -96,7 +96,7 @@ def debug_all_objects(): @cmdutils.register(debug=True) def debug_cache_stats(): """Print LRU cache stats.""" - config_info = utils.get_object('config').get.cache_info() + config_info = objreg.get('config').get.cache_info() style_info = style.get_stylesheet.cache_info() log.misc.debug('config: {}'.format(config_info)) log.misc.debug('style: {}'.format(style_info)) @@ -105,4 +105,4 @@ def debug_cache_stats(): @cmdutils.register(debug=True) def debug_console(): """Show the debugging console.""" - utils.get_object('debug-console').show() + objreg.get('debug-console').show() diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index b8e306ff9..387c53d90 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -39,9 +39,6 @@ import qutebrowser from qutebrowser.utils import qtutils, log -_UNSET = object() - - def elide(text, length): """Elide text so it uses a maximum of length chars.""" if length < 1: @@ -581,38 +578,3 @@ def is_enum(obj): return issubclass(obj, enum.Enum) except TypeError: return False - - -def get_object(name, default=_UNSET): - """Helper function to get an object. - - Args: - default: A default to return if the object does not exist. - """ - try: - return QCoreApplication.instance().registry[name] - except KeyError: - if default is not _UNSET: - return default - else: - raise - - -def register_object(name, obj, update=False): - """Helper function to register an object. - - Args: - name: The name the object will be registered as. - obj: The object to register. - update: If True, allows to update an already registered object. - """ - registry = QCoreApplication.instance().registry - if not update and name in registry: - raise KeyError("Object '{}' is already registered ({})!".format( - name, repr(registry[name]))) - registry[name] = obj - - -def delete_object(name): - """Helper function to unregister an object.""" - del QCoreApplication.instance().registry[name] diff --git a/qutebrowser/widgets/completion.py b/qutebrowser/widgets/completion.py index 68da7576a..5a237a824 100644 --- a/qutebrowser/widgets/completion.py +++ b/qutebrowser/widgets/completion.py @@ -29,7 +29,7 @@ from PyQt5.QtCore import pyqtSlot, pyqtSignal, Qt, QItemSelectionModel from qutebrowser.commands import cmdutils from qutebrowser.config import config, style from qutebrowser.widgets import completiondelegate -from qutebrowser.utils import completer, usertypes, qtutils, utils +from qutebrowser.utils import completer, usertypes, qtutils, objreg class CompletionView(QTreeView): @@ -92,7 +92,7 @@ class CompletionView(QTreeView): def __init__(self, parent=None): super().__init__(parent) completer_obj = completer.Completer(self) - utils.register_object('completer', completer_obj) + objreg.register('completer', completer_obj) self.enabled = config.get('completion', 'show') self._delegate = completiondelegate.CompletionItemDelegate(self) @@ -225,7 +225,7 @@ class CompletionView(QTreeView): def selectionChanged(self, selected, deselected): """Extend selectionChanged to call completers selection_changed.""" super().selectionChanged(selected, deselected) - utils.get_object('completer').selection_changed(selected, deselected) + objreg.get('completer').selection_changed(selected, deselected) def resizeEvent(self, e): """Extend resizeEvent to adjust column size.""" diff --git a/qutebrowser/widgets/crash.py b/qutebrowser/widgets/crash.py index 4bbb738b6..9a6fe6167 100644 --- a/qutebrowser/widgets/crash.py +++ b/qutebrowser/widgets/crash.py @@ -29,7 +29,7 @@ from PyQt5.QtCore import Qt, QSize from PyQt5.QtWidgets import (QDialog, QLabel, QTextEdit, QPushButton, QVBoxLayout, QHBoxLayout) -from qutebrowser.utils import version, log, utils +from qutebrowser.utils import version, log, utils, objreg class _CrashDialog(QDialog): @@ -130,7 +130,7 @@ class _CrashDialog(QDialog): except Exception: self._crash_info.append(("Version info", traceback.format_exc())) try: - conf = utils.get_object('config') + conf = objreg.get('config') self._crash_info.append(("Config", conf.dump_userconfig())) except Exception: self._crash_info.append(("Config", traceback.format_exc())) diff --git a/qutebrowser/widgets/mainwindow.py b/qutebrowser/widgets/mainwindow.py index 723bd7ad8..3455a2e56 100644 --- a/qutebrowser/widgets/mainwindow.py +++ b/qutebrowser/widgets/mainwindow.py @@ -27,7 +27,7 @@ from PyQt5.QtWidgets import QWidget, QVBoxLayout from qutebrowser.commands import cmdutils from qutebrowser.config import config -from qutebrowser.utils import message, log, usertypes, qtutils, utils +from qutebrowser.utils import message, log, usertypes, qtutils, objreg from qutebrowser.widgets import tabbedbrowser, completion, downloads from qutebrowser.widgets.statusbar import bar @@ -50,7 +50,7 @@ class MainWindow(QWidget): super().__init__(parent) self.setWindowTitle('qutebrowser') - state_config = utils.get_object('state-config') + state_config = objreg.get('state-config') try: data = state_config['geometry']['mainwindow'] log.init.debug("Restoring mainwindow from {}".format(data)) @@ -83,11 +83,11 @@ class MainWindow(QWidget): self._tabbed_browser = tabbedbrowser.TabbedBrowser() self._tabbed_browser.title_changed.connect(self.setWindowTitle) - utils.register_object('tabbed-browser', self._tabbed_browser) + objreg.register('tabbed-browser', self._tabbed_browser) self._vbox.addWidget(self._tabbed_browser) self._completion = completion.CompletionView(self) - utils.register_object('completion', self._completion) + objreg.register('completion', self._completion) self.status = bar.StatusBar() self._vbox.addWidget(self.status) diff --git a/qutebrowser/widgets/statusbar/bar.py b/qutebrowser/widgets/statusbar/bar.py index b2c51b1f6..c4a66020e 100644 --- a/qutebrowser/widgets/statusbar/bar.py +++ b/qutebrowser/widgets/statusbar/bar.py @@ -26,7 +26,7 @@ from PyQt5.QtCore import pyqtSignal, pyqtSlot, pyqtProperty, Qt from PyQt5.QtWidgets import QWidget, QHBoxLayout, QStackedLayout, QSizePolicy from qutebrowser.config import config, style -from qutebrowser.utils import usertypes, log, utils +from qutebrowser.utils import usertypes, log, objreg from qutebrowser.widgets.statusbar import (command, progress, keystring, percentage, url, prompt) from qutebrowser.widgets.statusbar import text as textwidget @@ -132,7 +132,7 @@ class StatusBar(QWidget): self._stack.setContentsMargins(0, 0, 0, 0) self._cmd = command.Command() - utils.register_object('status-command', self._cmd) + objreg.register('status-command', self._cmd) self._stack.addWidget(self._cmd) self.txt = textwidget.Text() @@ -376,7 +376,7 @@ class StatusBar(QWidget): @pyqtSlot(usertypes.KeyMode) def on_mode_entered(self, mode): """Mark certain modes in the commandline.""" - if mode in utils.get_object('mode-manager').passthrough: + if mode in objreg.get('mode-manager').passthrough: text = "-- {} MODE --".format(mode.name.upper()) self.txt.set_text(self.txt.Text.normal, text) if mode == usertypes.KeyMode.insert: @@ -385,7 +385,7 @@ class StatusBar(QWidget): @pyqtSlot(usertypes.KeyMode) def on_mode_left(self, mode): """Clear marked mode.""" - if mode in utils.get_object('mode-manager').passthrough: + if mode in objreg.get('mode-manager').passthrough: self.txt.set_text(self.txt.Text.normal, '') if mode == usertypes.KeyMode.insert: self._set_insert_active(False) diff --git a/qutebrowser/widgets/statusbar/command.py b/qutebrowser/widgets/statusbar/command.py index d6c38935e..a15a165d0 100644 --- a/qutebrowser/widgets/statusbar/command.py +++ b/qutebrowser/widgets/statusbar/command.py @@ -26,7 +26,7 @@ from qutebrowser.keyinput import modeman, modeparsers from qutebrowser.commands import runners, cmdexc, cmdutils from qutebrowser.widgets import misc from qutebrowser.models import cmdhistory -from qutebrowser.utils import usertypes, log, utils +from qutebrowser.utils import usertypes, log, objreg class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): @@ -74,7 +74,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): misc.CommandLineEdit.__init__(self, parent) misc.MinimalLineEditMixin.__init__(self) self.cursor_part = 0 - self.history.history = utils.get_object('command-history').data + self.history.history = objreg.get('command-history').data self._empty_item_idx = None self.textEdited.connect(self.on_text_edited) self.cursorPositionChanged.connect(self._update_cursor_part) @@ -172,7 +172,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): Args: text: The commandline to set. """ - url = utils.get_object('tabbed-browser').current_url().toString( + url = objreg.get('tabbed-browser').current_url().toString( QUrl.FullyEncoded | QUrl.RemovePassword) # FIXME we currently replace the URL in any place in the arguments, # rather than just replacing it if it is a dedicated argument. We could diff --git a/qutebrowser/widgets/statusbar/prompt.py b/qutebrowser/widgets/statusbar/prompt.py index 3122703dd..015cbd795 100644 --- a/qutebrowser/widgets/statusbar/prompt.py +++ b/qutebrowser/widgets/statusbar/prompt.py @@ -24,7 +24,7 @@ from PyQt5.QtWidgets import QHBoxLayout, QWidget, QLineEdit from qutebrowser.widgets import misc from qutebrowser.widgets.statusbar import textbase, prompter -from qutebrowser.utils import utils +from qutebrowser.utils import objreg class PromptLineEdit(misc.MinimalLineEditMixin, QLineEdit): @@ -66,7 +66,7 @@ class Prompt(QWidget): self._hbox.addWidget(self.lineedit) prompter_obj = prompter.Prompter(self) - utils.register_object('prompter', prompter_obj) + objreg.register('prompter', prompter_obj) def __repr__(self): return '<{}>'.format(self.__class__.__name__) diff --git a/qutebrowser/widgets/statusbar/prompter.py b/qutebrowser/widgets/statusbar/prompter.py index 16cacb85c..22b8874e6 100644 --- a/qutebrowser/widgets/statusbar/prompter.py +++ b/qutebrowser/widgets/statusbar/prompter.py @@ -26,7 +26,7 @@ from PyQt5.QtWidgets import QLineEdit from qutebrowser.keyinput import modeman from qutebrowser.commands import cmdutils -from qutebrowser.utils import usertypes, log, qtutils, utils +from qutebrowser.utils import usertypes, log, qtutils, objreg PromptContext = collections.namedtuple('PromptContext', @@ -280,7 +280,7 @@ class Prompter: self.question = question mode = self._display_question() question.aborted.connect(lambda: modeman.maybe_leave(mode, 'aborted')) - mode_manager = utils.get_object('mode-manager') + mode_manager = objreg.get('mode-manager') try: modeman.enter(mode, 'question asked') except modeman.ModeLockedError: diff --git a/qutebrowser/widgets/tabbedbrowser.py b/qutebrowser/widgets/tabbedbrowser.py index 82a5f5d51..bcad5e93b 100644 --- a/qutebrowser/widgets/tabbedbrowser.py +++ b/qutebrowser/widgets/tabbedbrowser.py @@ -31,7 +31,7 @@ from qutebrowser.commands import cmdexc, cmdutils from qutebrowser.keyinput import modeman from qutebrowser.widgets import tabwidget, webview from qutebrowser.browser import signalfilter, commands -from qutebrowser.utils import log, message, usertypes, utils, qtutils +from qutebrowser.utils import log, message, usertypes, utils, qtutils, objreg class TabbedBrowser(tabwidget.TabWidget): @@ -109,7 +109,7 @@ class TabbedBrowser(tabwidget.TabWidget): self.url_stack = [] self._filter = signalfilter.SignalFilter(self) dispatcher = commands.CommandDispatcher(self) - utils.register_object('command-dispatcher', dispatcher) + objreg.register('command-dispatcher', dispatcher) self._now_focused = None # FIXME adjust this to font size self.setIconSize(QSize(12, 12)) @@ -261,8 +261,8 @@ class TabbedBrowser(tabwidget.TabWidget): tab)) if tab is self._now_focused: self._now_focused = None - if tab is utils.get_object('last-focused-tab', None): - utils.delete_object('last-focused-tab') + if tab is objreg.get('last-focused-tab', None): + objreg.delete('last-focused-tab') if not tab.cur_url.isEmpty(): qtutils.ensure_valid(tab.cur_url) self.url_stack.append(tab.cur_url) @@ -525,8 +525,7 @@ class TabbedBrowser(tabwidget.TabWidget): tab.setFocus() modeman.maybe_leave(usertypes.KeyMode.hint, 'tab changed') if self._now_focused is not None: - utils.register_object('last-focused-tab', self._now_focused, - update=True) + objreg.register('last-focused-tab', self._now_focused, update=True) self._now_focused = tab self.current_tab_changed.emit(tab) self.title_changed.emit('{} - qutebrowser'.format(self.tabText(idx))) diff --git a/qutebrowser/widgets/tabwidget.py b/qutebrowser/widgets/tabwidget.py index e5af752e3..a43c993ab 100644 --- a/qutebrowser/widgets/tabwidget.py +++ b/qutebrowser/widgets/tabwidget.py @@ -31,7 +31,7 @@ from PyQt5.QtWidgets import (QTabWidget, QTabBar, QSizePolicy, QCommonStyle, QStyle, QStylePainter, QStyleOptionTab) from PyQt5.QtGui import QIcon, QPalette, QColor -from qutebrowser.utils import qtutils, utils +from qutebrowser.utils import qtutils, objreg from qutebrowser.config import config @@ -209,7 +209,7 @@ class TabBar(QTabBar): confwidth = str(config.get('tabs', 'width')) if confwidth.endswith('%'): perc = int(confwidth.rstrip('%')) - width = utils.get_object('main-window').width() * perc / 100 + width = objreg.get('main-window').width() * perc / 100 else: width = int(confwidth) size = QSize(max(minimum_size.width(), width), height) diff --git a/qutebrowser/widgets/webview.py b/qutebrowser/widgets/webview.py index 8ee2dc11c..f12eef131 100644 --- a/qutebrowser/widgets/webview.py +++ b/qutebrowser/widgets/webview.py @@ -26,7 +26,7 @@ from PyQt5.QtWebKitWidgets import QWebView, QWebPage from qutebrowser.config import config from qutebrowser.keyinput import modeman -from qutebrowser.utils import message, log, usertypes, utils, qtutils +from qutebrowser.utils import message, log, usertypes, utils, qtutils, objreg from qutebrowser.browser import webpage, hints, webelem from qutebrowser.commands import cmdexc @@ -358,7 +358,7 @@ class WebView(QWebView): self._set_load_status(LoadStatus.error) if not config.get('input', 'auto-insert-mode'): return - cur_mode = utils.get_object('mode-manager').mode() + cur_mode = objreg.get('mode-manager').mode() if cur_mode == usertypes.KeyMode.insert or not ok: return frame = self.page().currentFrame() From 3c2e584c2a2f55b10b5b90f98ce2aef0c73ab785 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 07:07:31 +0200 Subject: [PATCH 47/79] Make pylint shut up with _UNSET object. --- .pylintrc | 2 +- qutebrowser/utils/objreg.py | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.pylintrc b/.pylintrc index f161de012..8cb7a35fd 100644 --- a/.pylintrc +++ b/.pylintrc @@ -53,4 +53,4 @@ defining-attr-methods=__init__,__new__,setUp max-args=10 [TYPECHECK] -ignored-classes=WebElementWrapper,AnsiCodes +ignored-classes=WebElementWrapper,AnsiCodes,UnsetObject diff --git a/qutebrowser/utils/objreg.py b/qutebrowser/utils/objreg.py index d83727c9a..f663b02a3 100644 --- a/qutebrowser/utils/objreg.py +++ b/qutebrowser/utils/objreg.py @@ -26,7 +26,17 @@ import functools from PyQt5.QtCore import QCoreApplication, QObject -_UNSET = object() +class UnsetObject: + + """Class for an unset object. + + Only used (rather than object) so we can tell pylint to shut up about it. + """ + + __slots__ = () + + +_UNSET = UnsetObject() class ObjectRegistry(collections.UserDict): From aa646463b0f2dbf903c698c64c8793938c419130 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 07:10:17 +0200 Subject: [PATCH 48/79] Also use objreg API in app --- qutebrowser/app.py | 91 +++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 46 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 3688d202c..f5f44b821 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -86,7 +86,7 @@ class Application(QApplication): self.meta_registry = objreg.ObjectRegistry() self.registry = objreg.ObjectRegistry() self.meta_registry['global'] = self.registry - self.registry['app'] = self + objreg.register('app', self) self._shutting_down = False self._keyparsers = None self._crashdlg = None @@ -95,7 +95,7 @@ class Application(QApplication): sys.excepthook = self._exception_hook self._args = args - self.registry['args'] = args + objreg.register('args', args) log.init.debug("Starting init...") self._init_misc() utils.actute_warning() @@ -105,7 +105,7 @@ class Application(QApplication): self._handle_segfault() log.init.debug("Initializing modes...") self._init_modes() - mode_manager = self.registry['mode-manager'] + mode_manager = objreg.get('mode-manager') log.init.debug("Initializing websettings...") websettings.init() log.init.debug("Initializing quickmarks...") @@ -118,24 +118,24 @@ class Application(QApplication): utilcmds.init() log.init.debug("Initializing cookies...") cookie_jar = cookies.CookieJar(self) - self.registry['cookie-jar'] = cookie_jar + objreg.register('cookie-jar', cookie_jar) log.init.debug("Initializing cache...") diskcache = cache.DiskCache(self) - self.registry['cache'] = diskcache + objreg.register('cache', diskcache) log.init.debug("Initializing commands...") self._commandrunner = runners.CommandRunner() log.init.debug("Initializing search...") search_runner = runners.SearchRunner(self) - self.registry['search-runner'] = search_runner + objreg.register('search-runner', search_runner) log.init.debug("Initializing downloads...") download_manager = downloads.DownloadManager(self) - self.registry['download-manager'] = download_manager + objreg.register('download-manager', download_manager) log.init.debug("Initializing main window...") main_window = mainwindow.MainWindow() - self.registry['main-window'] = main_window + objreg.register('main-window', main_window) log.init.debug("Initializing debug console...") debug_console = console.ConsoleWidget() - self.registry['debug-console'] = debug_console + objreg.register('debug-console', debug_console) log.init.debug("Initializing eventfilter...") self.installEventFilter(mode_manager) self.setQuitOnLastWindowClosed(False) @@ -190,7 +190,7 @@ class Application(QApplication): # We didn't really initialize much so far, so we just quit hard. sys.exit(1) else: - self.registry['config'] = config_obj + objreg.register('config', config_obj) try: key_config = keyconfparser.KeyConfigParser(confdir, 'keys.conf') except keyconfparser.KeyConfigError as e: @@ -205,12 +205,12 @@ class Application(QApplication): # We didn't really initialize much so far, so we just quit hard. sys.exit(1) else: - self.registry['key-config'] = key_config + objreg.register('key-config', key_config) state_config = iniparsers.ReadWriteConfigParser(confdir, 'state') - self.registry['state-config'] = state_config + objreg.register('state-config', state_config) command_history = lineparser.LineConfigParser( confdir, 'cmd_history', ('completion', 'history-length')) - self.registry['command-history'] = command_history + objreg.register('command-history', command_history) def _init_modes(self): """Inizialize the mode manager and the keyparsers.""" @@ -231,7 +231,7 @@ class Application(QApplication): modeparsers.PromptKeyParser(self), } mode_manager = modeman.ModeManager(self) - self.registry['mode-manager'] = mode_manager + objreg.register('mode-manager', mode_manager) mode_manager.register(utypes.KeyMode.normal, self._keyparsers[utypes.KeyMode.normal].handle) mode_manager.register(utypes.KeyMode.hint, @@ -266,9 +266,9 @@ class Application(QApplication): self.setApplicationName("qutebrowser") self.setApplicationVersion(qutebrowser.__version__) message_bridge = message.MessageBridge(self) - self.registry['message-bridge'] = message_bridge + objreg.register('message-bridge', message_bridge) readline_bridge = readline.ReadlineBridge() - self.registry['readline-bridge'] = readline_bridge + objreg.register('readline-bridge', readline_bridge) def _handle_segfault(self): """Handle a segfault from a previous run.""" @@ -331,7 +331,7 @@ class Application(QApplication): # we make sure the GUI is refreshed here, so the start seems faster. self.processEvents(QEventLoop.ExcludeUserInputEvents | QEventLoop.ExcludeSocketNotifiers) - tabbed_browser = self.registry['tabbed-browser'] + tabbed_browser = objreg.get('tabbed-browser') for cmd in self._args.command: if cmd.startswith(':'): log.init.debug("Startup cmd {}".format(cmd)) @@ -368,27 +368,27 @@ class Application(QApplication): timer = utypes.Timer(self, 'python_hacks') timer.start(500) timer.timeout.connect(lambda: None) - self.registry['python-hack-timer'] = timer + objreg.register('python-hack-timer', timer) def _connect_signals(self): """Connect all signals to their slots.""" # pylint: disable=too-many-statements, too-many-locals # syntactic sugar kp = self._keyparsers - main_window = self.registry['main-window'] + main_window = objreg.get('main-window') status = main_window.status - completion = self.registry['completion'] - tabs = self.registry['tabbed-browser'] - cmd = self.registry['status-command'] - completer = self.registry['completer'] - search_runner = self.registry['search-runner'] - message_bridge = self.registry['message-bridge'] - mode_manager = self.registry['mode-manager'] - prompter = self.registry['prompter'] - command_history = self.registry['command-history'] - download_manager = self.registry['download-manager'] - config_obj = self.registry['config'] - key_config = self.registry['key-config'] + completion = objreg.get('completion') + tabs = objreg.get('tabbed-browser') + cmd = objreg.get('status-command') + completer = objreg.get('completer') + search_runner = objreg.get('search-runner') + message_bridge = objreg.get('message-bridge') + mode_manager = objreg.get('mode-manager') + prompter = objreg.get('prompter') + command_history = objreg.get('command-history') + download_manager = objreg.get('download-manager') + config_obj = objreg.get('config') + key_config = objreg.get('key-config') # misc self.lastWindowClosed.connect(self.shutdown) @@ -519,7 +519,7 @@ class Application(QApplication): A list of open pages, or an empty list. """ try: - tabbed_browser = self.registry['tabbed-browser'] + tabbed_browser = objreg.get('tabbed-browser') except KeyError: return [] pages = [] @@ -535,8 +535,8 @@ class Application(QApplication): def _save_geometry(self): """Save the window geometry to the state config.""" - state_config = self.registry['state-config'] - data = bytes(self.registry['main-window'].saveGeometry()) + state_config = objreg.get('state-config') + data = bytes(objreg.get('main-window').saveGeometry()) geom = base64.b64encode(data).decode('ASCII') try: state_config.add_section('geometry') @@ -591,7 +591,7 @@ class Application(QApplication): pages = [] try: - history = self.registry['status-command'].history[-5:] + history = objreg.get('status-command').history[-5:] except Exception: log.destroy.exception("Error while getting history: {}") history = [] @@ -673,14 +673,13 @@ class Application(QApplication): except Exception: # pylint: disable=broad-except out = traceback.format_exc() qutescheme.pyeval_output = out - self.registry['tabbed-browser'].openurl( - QUrl('qute:pyeval'), newtab=True) + objreg.get('tabbed-browser').openurl(QUrl('qute:pyeval'), newtab=True) @cmdutils.register(instance='app') def report(self): """Report a bug in qutebrowser.""" pages = self._recover_pages() - history = self.registry['status-command'].history[-5:] + history = objreg.get('status-command').history[-5:] objects = self.get_all_objects() self._crashdlg = crash.ReportDialog(pages, history, objects) self._crashdlg.show() @@ -736,7 +735,7 @@ class Application(QApplication): return self._shutting_down = True log.destroy.debug("Shutting down with status {}...".format(status)) - if self.registry['prompter'].shutdown(): + if objreg.get('prompter').shutdown(): # If shutdown was called while we were asking a question, we're in # a still sub-eventloop (which gets quitted now) and not in the # main one. @@ -758,18 +757,18 @@ class Application(QApplication): # Remove eventfilter try: log.destroy.debug("Removing eventfilter...") - self.removeEventFilter(self.registry['mode-manager']) + self.removeEventFilter(objreg.get('mode-manager')) except KeyError: pass # Close all tabs try: log.destroy.debug("Closing tabs...") - self.registry['tabbed-browser'].shutdown() + objreg.get('tabbed-browser').shutdown() except KeyError: pass # Save everything try: - config_obj = self.registry['config'] + config_obj = objreg.get('config') except KeyError: log.destroy.debug("Config not initialized yet, so not saving " "anything.") @@ -778,7 +777,7 @@ class Application(QApplication): if config.get('general', 'auto-save-config'): to_save.append(("config", config_obj.save)) try: - key_config = self.registry['key-config'] + key_config = objreg.get('key-config') except KeyError: pass else: @@ -786,19 +785,19 @@ class Application(QApplication): to_save += [("window geometry", self._save_geometry), ("quickmarks", quickmarks.save)] try: - command_history = self.registry['command-history'] + command_history = objreg.get('command-history') except KeyError: pass else: to_save.append(("command history", command_history.save)) try: - state_config = self.registry['state-config'] + state_config = objreg.get('state-config') except KeyError: pass else: to_save.append(("window geometry", state_config.save)) try: - cookie_jar = self.registry['cookie-jar'] + cookie_jar = objreg.get('cookie-jar') except KeyError: pass else: From b0a9ecf0943da554002ba1987a0a463c80dc2dee Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 07:13:53 +0200 Subject: [PATCH 49/79] Detach object registry from application. --- qutebrowser/app.py | 4 +--- qutebrowser/utils/objreg.py | 16 +++++++++------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index f5f44b821..255f8ab7b 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -53,7 +53,6 @@ class Application(QApplication): """Main application instance. Attributes: - registry: The object registry of global objects. meta_registry: The object registry of object registries. _args: ArgumentParser instance. _commandrunner: The main CommandRunner instance. @@ -84,8 +83,7 @@ class Application(QApplication): 'main': False, } self.meta_registry = objreg.ObjectRegistry() - self.registry = objreg.ObjectRegistry() - self.meta_registry['global'] = self.registry + self.meta_registry['global'] = objreg.global_registry objreg.register('app', self) self._shutting_down = False self._keyparsers = None diff --git a/qutebrowser/utils/objreg.py b/qutebrowser/utils/objreg.py index f663b02a3..7636a0f0b 100644 --- a/qutebrowser/utils/objreg.py +++ b/qutebrowser/utils/objreg.py @@ -23,7 +23,7 @@ import collections import functools -from PyQt5.QtCore import QCoreApplication, QObject +from PyQt5.QtCore import QObject class UnsetObject: @@ -70,6 +70,9 @@ class ObjectRegistry(collections.UserDict): return lines +global_registry = ObjectRegistry() + + def get(name, default=_UNSET): """Helper function to get an object. @@ -77,7 +80,7 @@ def get(name, default=_UNSET): default: A default to return if the object does not exist. """ try: - return QCoreApplication.instance().registry[name] + return global_registry[name] except KeyError: if default is not _UNSET: return default @@ -93,13 +96,12 @@ def register(name, obj, update=False): obj: The object to register. update: If True, allows to update an already registered object. """ - registry = QCoreApplication.instance().registry - if not update and name in registry: + if not update and name in global_registry: raise KeyError("Object '{}' is already registered ({})!".format( - name, repr(registry[name]))) - registry[name] = obj + name, repr(global_registry[name]))) + global_registry[name] = obj def delete(name): """Helper function to unregister an object.""" - del QCoreApplication.instance().registry[name] + del global_registry[name] From 387622623d682751b213f9d789ac0e45d32d3c57 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 07:27:32 +0200 Subject: [PATCH 50/79] Detach meta object registry from application. --- qutebrowser/app.py | 5 +---- qutebrowser/utils/objreg.py | 4 ++++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 255f8ab7b..1417d6791 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -53,7 +53,6 @@ class Application(QApplication): """Main application instance. Attributes: - meta_registry: The object registry of object registries. _args: ArgumentParser instance. _commandrunner: The main CommandRunner instance. _keyparsers: A mapping from modes to keyparsers. @@ -82,8 +81,6 @@ class Application(QApplication): 'tabs': False, 'main': False, } - self.meta_registry = objreg.ObjectRegistry() - self.meta_registry['global'] = objreg.global_registry objreg.register('app', self) self._shutting_down = False self._keyparsers = None @@ -480,7 +477,7 @@ class Application(QApplication): """Get all registered objects in all registries as a string.""" blocks = [] lines = [] - for name, registry in self.meta_registry.items(): + for name, registry in objreg.meta_registry.items(): blocks.append((name, registry.dump_objects())) for name, data in blocks: lines.append("{} object registry - {} objects:".format( diff --git a/qutebrowser/utils/objreg.py b/qutebrowser/utils/objreg.py index 7636a0f0b..45bc5fed3 100644 --- a/qutebrowser/utils/objreg.py +++ b/qutebrowser/utils/objreg.py @@ -70,7 +70,11 @@ class ObjectRegistry(collections.UserDict): return lines +# The registry for global objects global_registry = ObjectRegistry() +# The object registry of object registries. +meta_registry = ObjectRegistry() +meta_registry['global'] = global_registry def get(name, default=_UNSET): From fd9c4b860a7fbf6b69e20da856fcf095eb750bfb Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 07:32:05 +0200 Subject: [PATCH 51/79] Use object registry for url_stack. --- qutebrowser/browser/commands.py | 5 +++-- qutebrowser/widgets/tabbedbrowser.py | 7 ++++--- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 8e3351148..5c36cdd4e 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -531,8 +531,9 @@ class CommandDispatcher: @cmdutils.register(instance='command-dispatcher') def undo(self): """Re-open a closed tab (optionally skipping [count] closed tabs).""" - if self._tabs.url_stack: - self._tabs.tabopen(self._tabs.url_stack.pop()) + url_stack = objreg.get('url-stack', None) + if url_stack: + self._tabs.tabopen(url_stack.pop()) else: raise cmdexc.CommandError("Nothing to undo!") diff --git a/qutebrowser/widgets/tabbedbrowser.py b/qutebrowser/widgets/tabbedbrowser.py index bcad5e93b..1348b9ae7 100644 --- a/qutebrowser/widgets/tabbedbrowser.py +++ b/qutebrowser/widgets/tabbedbrowser.py @@ -53,7 +53,7 @@ class TabbedBrowser(tabwidget.TabWidget): _tab_insert_idx_left: Where to insert a new tab with tabbar -> new-tab-position set to 'left'. _tab_insert_idx_right: Same as above, for 'right'. - url_stack: Stack of URLs of closed tabs. + _url_stack: Stack of URLs of closed tabs. Signals: cur_progress: Progress of the current tab changed (loadProgress). @@ -106,7 +106,8 @@ class TabbedBrowser(tabwidget.TabWidget): self.currentChanged.connect(self.on_current_changed) self.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) self._tabs = [] - self.url_stack = [] + self._url_stack = [] + objreg.register('url-stack', self._url_stack) self._filter = signalfilter.SignalFilter(self) dispatcher = commands.CommandDispatcher(self) objreg.register('command-dispatcher', dispatcher) @@ -265,7 +266,7 @@ class TabbedBrowser(tabwidget.TabWidget): objreg.delete('last-focused-tab') if not tab.cur_url.isEmpty(): qtutils.ensure_valid(tab.cur_url) - self.url_stack.append(tab.cur_url) + self._url_stack.append(tab.cur_url) tab.shutdown() self._tabs.remove(tab) self.removeTab(idx) From 5e8144fafa8e554898ff3100d4461e05a1ea88ac Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 19:53:31 +0200 Subject: [PATCH 52/79] Move cntwidget to commands. --- qutebrowser/browser/commands.py | 30 +++++++++++++++++++++++----- qutebrowser/widgets/tabbedbrowser.py | 19 ------------------ 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 5c36cdd4e..da0e9a9dd 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -92,6 +92,26 @@ class CommandDispatcher: widget = self._current_widget() widget.openurl(url) + def _cntwidget(self, count=None): + """Return a widget based on a count/idx. + + Args: + count: The tab index, or None. + + Return: + The current widget if count is None. + The widget with the given tab ID if count is given. + None if no widget was found. + """ + if count is None: + return self._tabs.currentWidget() + elif 1 <= count <= self._tabs.count(): + cmdutils.check_overflow(count + 1, 'int') + return self._tabs.widget(count - 1) + else: + return None + + def _scroll_percent(self, perc=None, count=None, orientation=None): """Inner logic for scroll_percent_(x|y). @@ -170,7 +190,7 @@ class CommandDispatcher: quit: If last tab was closed and last-close in config is set to quit. """ - tab = self._tabs.cntwidget(count) + tab = self._cntwidget(count) if tab is None: return self._tabs.close_tab(tab) @@ -195,7 +215,7 @@ class CommandDispatcher: elif bg: self._tabs.tabopen(url, background=True, explicit=True) else: - curtab = self._tabs.cntwidget(count) + curtab = self._cntwidget(count) if curtab is None: if count is None: # We want to open a URL in the current tab, but none exists @@ -214,7 +234,7 @@ class CommandDispatcher: Args: count: The tab index to reload, or None. """ - tab = self._tabs.cntwidget(count) + tab = self._cntwidget(count) if tab is not None: tab.reload() @@ -225,7 +245,7 @@ class CommandDispatcher: Args: count: The tab index to stop, or None. """ - tab = self._tabs.cntwidget(count) + tab = self._cntwidget(count) if tab is not None: tab.stop() @@ -241,7 +261,7 @@ class CommandDispatcher: # WORKAROUND (remove this when we bump the requirements to 5.3.0) raise cmdexc.CommandError( "Printing on Qt < 5.3.0 on Windows is broken, please upgrade!") - tab = self._tabs.cntwidget(count) + tab = self._cntwidget(count) if tab is not None: if preview: diag = QPrintPreviewDialog() diff --git a/qutebrowser/widgets/tabbedbrowser.py b/qutebrowser/widgets/tabbedbrowser.py index 1348b9ae7..171985933 100644 --- a/qutebrowser/widgets/tabbedbrowser.py +++ b/qutebrowser/widgets/tabbedbrowser.py @@ -174,25 +174,6 @@ class TabbedBrowser(tabwidget.TabWidget): page.windowCloseRequested.connect( functools.partial(self.on_window_close_requested, tab)) - def cntwidget(self, count=None): - """Return a widget based on a count/idx. - - Args: - count: The tab index, or None. - - Return: - The current widget if count is None. - The widget with the given tab ID if count is given. - None if no widget was found. - """ - if count is None: - return self.currentWidget() - elif 1 <= count <= self.count(): - cmdutils.check_overflow(count + 1, 'int') - return self.widget(count - 1) - else: - return None - def current_url(self): """Get the URL of the current tab. From b11910032107c2b5203bf48dd21c90728a18c790 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 20:21:43 +0200 Subject: [PATCH 53/79] Get rid of _tabs attribute in CommandDispatcher. --- qutebrowser/browser/commands.py | 130 ++++++++++++++------------- qutebrowser/widgets/tabbedbrowser.py | 4 +- 2 files changed, 72 insertions(+), 62 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index da0e9a9dd..25e6e0d8f 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -55,21 +55,31 @@ class CommandDispatcher: _editor: The ExternalEditor object. """ - def __init__(self, parent): - """Constructor. - - Args: - parent: The TabbedBrowser for this dispatcher. - """ - self._tabs = parent + def __init__(self): self._editor = None def __repr__(self): return '<{}>'.format(self.__class__.__name__) + def _count(self): + """Convenience method to get the widget count.""" + return objreg.get('tabbed-browser').count() + + def _set_current_index(self, idx): + """Convenience method to set the current widget index.""" + return objreg.get('tabbed-browser').setCurrentIndex(idx) + + def _current_index(self): + """Convenience method to get the current widget index.""" + return objreg.get('tabbed-browser').currentIndex() + + def _current_url(self): + """Convenience method to get the current url.""" + return objreg.get('tabbed-browser').current_url() + def _current_widget(self): """Get the currently active widget from a command.""" - widget = self._tabs.currentWidget() + widget = objreg.get('tabbed-browser').currentWidget() if widget is None: raise cmdexc.CommandError("No WebView available yet!") return widget @@ -82,12 +92,13 @@ class CommandDispatcher: tab: Whether to open in a new tab. background: Whether to open in the background. """ + tabbed_browser = objreg.get('tabbed-browser') if tab and background: raise cmdexc.CommandError("Only one of -t/-b can be given!") elif tab: - self._tabs.tabopen(url, background=False, explicit=True) + tabbed_browser.tabopen(url, background=False, explicit=True) elif background: - self._tabs.tabopen(url, background=True, explicit=True) + tabbed_browser.tabopen(url, background=True, explicit=True) else: widget = self._current_widget() widget.openurl(url) @@ -103,15 +114,15 @@ class CommandDispatcher: The widget with the given tab ID if count is given. None if no widget was found. """ + tabbed_browser = objreg.get('tabbed-browser') if count is None: - return self._tabs.currentWidget() - elif 1 <= count <= self._tabs.count(): + return tabbed_browser.currentWidget() + elif 1 <= count <= self._count(): cmdutils.check_overflow(count + 1, 'int') - return self._tabs.widget(count - 1) + return tabbed_browser.widget(count - 1) else: return None - def _scroll_percent(self, perc=None, count=None, orientation=None): """Inner logic for scroll_percent_(x|y). @@ -140,7 +151,7 @@ class CommandDispatcher: if idx is None: return 0 elif idx == 0: - return self._tabs.count() - 1 + return self._count() - 1 else: return idx - 1 @@ -156,9 +167,9 @@ class CommandDispatcher: # gets called from tab_move which has delta set to None by default. delta = 1 if direction == '-': - return self._tabs.currentIndex() - delta + return self._current_index() - delta elif direction == '+': - return self._tabs.currentIndex() + delta + return self._current_index() + delta def _tab_focus_last(self): """Select the tab which was last focused.""" @@ -166,10 +177,10 @@ class CommandDispatcher: tab = objreg.get('last-focused-tab') except KeyError: raise cmdexc.CommandError("No last focused tab!") - idx = self._tabs.indexOf(tab) + idx = objreg.get('tabbed-browser').indexOf(tab) if idx == -1: raise cmdexc.CommandError("Last focused tab vanished!") - self._tabs.setCurrentIndex(idx) + self._set_current_index(idx) def _editor_cleanup(self, oshandle, filename): """Clean up temporary file when the editor was closed.""" @@ -193,7 +204,7 @@ class CommandDispatcher: tab = self._cntwidget(count) if tab is None: return - self._tabs.close_tab(tab) + objreg.get('tabbed-browser').close_tab(tab) @cmdutils.register(instance='command-dispatcher', name='open', split=False) @@ -210,17 +221,15 @@ class CommandDispatcher: url = urlutils.fuzzy_url(url) except urlutils.FuzzyUrlError as e: raise cmdexc.CommandError(e) - if tab: - self._tabs.tabopen(url, background=False, explicit=True) - elif bg: - self._tabs.tabopen(url, background=True, explicit=True) + if tab or bg: + self._open(url, tab, bg) else: curtab = self._cntwidget(count) if curtab is None: if count is None: # We want to open a URL in the current tab, but none exists # yet. - self._tabs.tabopen(url) + objreg.get('tabbed-browser').tabopen(url) else: # Explicit count with a tab that doesn't exist. return @@ -334,8 +343,8 @@ class CommandDispatcher: frame = widget.page().mainFrame() if frame is None: raise cmdexc.CommandError("No frame focused!") - widget.hintmanager.start(frame, self._tabs.current_url(), group, - target, *args) + widget.hintmanager.start(frame, self._current_url(), group, target, + *args) @cmdutils.register(instance='command-dispatcher', hide=True) def follow_hint(self): @@ -412,7 +421,7 @@ class CommandDispatcher: """ widget = self._current_widget() frame = widget.page().currentFrame() - url = self._tabs.current_url() + url = self._current_url() if frame is None: raise cmdexc.CommandError("No frame focused!") if where == 'prev': @@ -487,9 +496,9 @@ class CommandDispatcher: """ clipboard = QApplication.clipboard() if title: - s = self._tabs.tabText(self._tabs.currentIndex()) + s = objreg.get('tabbed-browser').tabText(self._current_index()) else: - s = self._tabs.current_url().toString( + s = self._current_url().toString( QUrl.FullyEncoded | QUrl.RemovePassword) if sel and clipboard.supportsSelection(): mode = QClipboard.Selection @@ -543,17 +552,18 @@ class CommandDispatcher: @cmdutils.register(instance='command-dispatcher') def tab_only(self): """Close all tabs except for the current one.""" - for tab in self._tabs.widgets(): + tabbed_browser = objreg.get('tabbed-browser') + for tab in tabbed_browser.widgets(): if tab is self._current_widget(): continue - self._tabs.close_tab(tab) + tabbed_browser.close_tab(tab) @cmdutils.register(instance='command-dispatcher') def undo(self): """Re-open a closed tab (optionally skipping [count] closed tabs).""" url_stack = objreg.get('url-stack', None) if url_stack: - self._tabs.tabopen(url_stack.pop()) + objreg.get('tabbed-browser').tabopen(url_stack.pop()) else: raise cmdexc.CommandError("Nothing to undo!") @@ -564,11 +574,11 @@ class CommandDispatcher: Args: count: How many tabs to switch back. """ - newidx = self._tabs.currentIndex() - count + newidx = self._current_index() - count if newidx >= 0: - self._tabs.setCurrentIndex(newidx) + self._set_current_index(newidx) elif config.get('tabs', 'wrap'): - self._tabs.setCurrentIndex(newidx % self._tabs.count()) + self._set_current_index(newidx % self._count()) else: raise cmdexc.CommandError("First tab") @@ -579,11 +589,11 @@ class CommandDispatcher: Args: count: How many tabs to switch forward. """ - newidx = self._tabs.currentIndex() + count - if newidx < self._tabs.count(): - self._tabs.setCurrentIndex(newidx) + newidx = self._current_index() + count + if newidx < self._count(): + self._set_current_index(newidx) elif config.get('tabs', 'wrap'): - self._tabs.setCurrentIndex(newidx % self._tabs.count()) + self._set_current_index(newidx % self._count()) else: raise cmdexc.CommandError("Last tab") @@ -627,12 +637,12 @@ class CommandDispatcher: return try: idx = cmdutils.arg_or_count(index, count, default=1, - countzero=self._tabs.count()) + countzero=self._count()) except ValueError as e: raise cmdexc.CommandError(e) cmdutils.check_overflow(idx + 1, 'int') - if 1 <= idx <= self._tabs.count(): - self._tabs.setCurrentIndex(idx - 1) + if 1 <= idx <= self._count(): + self._set_current_index(idx - 1) else: raise cmdexc.CommandError("There's no tab with index {}!".format( idx)) @@ -658,22 +668,23 @@ class CommandDispatcher: else: raise cmdexc.CommandError("Invalid direction '{}'!".format( direction)) - if not 0 <= new_idx < self._tabs.count(): + if not 0 <= new_idx < self._count(): raise cmdexc.CommandError("Can't move tab to position {}!".format( new_idx)) + tabbed_browser = objreg.get('tabbed-browser') tab = self._current_widget() - cur_idx = self._tabs.currentIndex() - icon = self._tabs.tabIcon(cur_idx) - label = self._tabs.tabText(cur_idx) + cur_idx = self._current_index() + icon = tabbed_browser.tabIcon(cur_idx) + label = tabbed_browser.tabText(cur_idx) cmdutils.check_overflow(cur_idx, 'int') cmdutils.check_overflow(new_idx, 'int') - self._tabs.setUpdatesEnabled(False) + tabbed_browser.setUpdatesEnabled(False) try: - self._tabs.removeTab(cur_idx) - self._tabs.insertTab(new_idx, tab, icon, label) - self._tabs.setCurrentIndex(new_idx) + tabbed_browser.removeTab(cur_idx) + tabbed_browser.insertTab(new_idx, tab, icon, label) + self._set_current_index(new_idx) finally: - self._tabs.setUpdatesEnabled(True) + tabbed_browser.setUpdatesEnabled(True) @cmdutils.register(instance='command-dispatcher', split=False) def spawn(self, *args): @@ -706,13 +717,12 @@ class CommandDispatcher: cmd: The userscript to run. args: Arguments to pass to the userscript. """ - url = self._tabs.current_url() - userscripts.run(cmd, *args, url=url) + userscripts.run(cmd, *args, url=self._current_url()) @cmdutils.register(instance='command-dispatcher') def quickmark_save(self): """Save the current page as a quickmark.""" - quickmarks.prompt_save(self._tabs.current_url()) + quickmarks.prompt_save(self._current_url()) @cmdutils.register(instance='command-dispatcher') def quickmark_load(self, name, tab=False, bg=False): @@ -756,7 +766,8 @@ class CommandDispatcher: def download_page(self): """Download the current page.""" page = self._current_widget().page() - self._tabs.download_get.emit(self._tabs.current_url(), page) + tabbed_browser = objreg.get('tabbed-browser') + tabbed_browser.download_get.emit(self._current_url(), page) @cmdutils.register(instance='command-dispatcher') def view_source(self): @@ -767,14 +778,13 @@ class CommandDispatcher: if widget.viewing_source: raise cmdexc.CommandError("Already viewing source!") frame = widget.page().currentFrame() - url = self._tabs.current_url() html = frame.toHtml() lexer = pygments.lexers.HtmlLexer() formatter = pygments.formatters.HtmlFormatter( full=True, linenos='table') highlighted = pygments.highlight(html, lexer, formatter) - tab = self._tabs.tabopen(explicit=True) - tab.setHtml(highlighted, url) + tab = objreg.get('tabbed-browser').tabopen(explicit=True) + tab.setHtml(highlighted, self._current_url()) tab.viewing_source = True @cmdutils.register(instance='command-dispatcher', name='help', @@ -840,7 +850,7 @@ class CommandDispatcher: text = str(elem) else: text = elem.evaluateJavaScript('this.value') - self._editor = editor.ExternalEditor(self._tabs) + self._editor = editor.ExternalEditor(objreg.get('tabbed-browser')) self._editor.editing_finished.connect( partial(self.on_editing_finished, elem)) self._editor.edit(text) diff --git a/qutebrowser/widgets/tabbedbrowser.py b/qutebrowser/widgets/tabbedbrowser.py index 171985933..ef91eb665 100644 --- a/qutebrowser/widgets/tabbedbrowser.py +++ b/qutebrowser/widgets/tabbedbrowser.py @@ -27,7 +27,7 @@ from PyQt5.QtGui import QIcon from PyQt5.QtWebKitWidgets import QWebPage from qutebrowser.config import config -from qutebrowser.commands import cmdexc, cmdutils +from qutebrowser.commands import cmdexc from qutebrowser.keyinput import modeman from qutebrowser.widgets import tabwidget, webview from qutebrowser.browser import signalfilter, commands @@ -109,7 +109,7 @@ class TabbedBrowser(tabwidget.TabWidget): self._url_stack = [] objreg.register('url-stack', self._url_stack) self._filter = signalfilter.SignalFilter(self) - dispatcher = commands.CommandDispatcher(self) + dispatcher = commands.CommandDispatcher() objreg.register('command-dispatcher', dispatcher) self._now_focused = None # FIXME adjust this to font size From 9868721cc739f470db6d4b92b62bc03fc6390127 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 20:27:05 +0200 Subject: [PATCH 54/79] Use object registry for webview.tabbedbrowser. --- qutebrowser/browser/webpage.py | 4 ++-- qutebrowser/widgets/webview.py | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/qutebrowser/browser/webpage.py b/qutebrowser/browser/webpage.py index 74c5f834c..bdfef0848 100644 --- a/qutebrowser/browser/webpage.py +++ b/qutebrowser/browser/webpage.py @@ -301,10 +301,10 @@ class BrowserPage(QWebPage): log.webview.debug(url.errorString()) return False if self._view.open_target == usertypes.ClickTarget.tab: - self._view.tabbedbrowser.tabopen(url, False) + objreg.get('tabbed-browser').tabopen(url, False) return False elif self._view.open_target == usertypes.ClickTarget.tab_bg: - self._view.tabbedbrowser.tabopen(url, True) + objreg.get('tabbed-browser').tabopen(url, True) return False else: self.change_title.emit(urlstr) diff --git a/qutebrowser/widgets/webview.py b/qutebrowser/widgets/webview.py index f12eef131..a24d49439 100644 --- a/qutebrowser/widgets/webview.py +++ b/qutebrowser/widgets/webview.py @@ -43,9 +43,6 @@ class WebView(QWebView): Attributes: hintmanager: The HintManager instance for this view. - tabbedbrowser: The TabbedBrowser this WebView is part of. - We need this rather than signals to make createWindow - work. progress: loading progress of this page. scroll_pos: The current scroll position as (x%, y%) tuple. statusbar_message: The current javscript statusbar message. @@ -77,11 +74,10 @@ class WebView(QWebView): load_status_changed = pyqtSignal(str) url_text_changed = pyqtSignal(str) - def __init__(self, parent): + def __init__(self, parent=None): super().__init__(parent) self.load_status = LoadStatus.none self._check_insertmode = False - self.tabbedbrowser = parent self.inspector = None self.scroll_pos = (-1, -1) self.statusbar_message = '' @@ -404,7 +400,7 @@ class WebView(QWebView): if wintype == QWebPage.WebModalDialog: log.webview.warning("WebModalDialog requested, but we don't " "support that!") - return self.tabbedbrowser.tabopen() + return objreg.get('tabbed-browser').tabopen() def paintEvent(self, e): """Extend paintEvent to emit a signal if the scroll position changed. From 7e8e9ee21f53ccc385cd1c17dd1954c7a7b288af Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 20:29:38 +0200 Subject: [PATCH 55/79] Get rid of _page attribute of WebView --- qutebrowser/widgets/webview.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/qutebrowser/widgets/webview.py b/qutebrowser/widgets/webview.py index a24d49439..decb87c65 100644 --- a/qutebrowser/widgets/webview.py +++ b/qutebrowser/widgets/webview.py @@ -51,7 +51,6 @@ class WebView(QWebView): open_target: Where to open the next tab ("normal", "tab", "tab_bg") viewing_source: Whether the webview is currently displaying source code. - _page: The QWebPage behind the view _cur_url: The current URL (accessed via cur_url property). _has_ssl_errors: Whether SSL errors occured during loading. _zoom: A NeighborList with the zoom levels. @@ -90,16 +89,16 @@ class WebView(QWebView): self._cur_url = None self.cur_url = QUrl() self.progress = 0 - self._page = webpage.BrowserPage(self) - self.setPage(self._page) + page = webpage.BrowserPage(self) + self.setPage(page) self.hintmanager = hints.HintManager(self) self.hintmanager.mouse_event.connect(self.on_mouse_event) self.hintmanager.set_open_target.connect(self.set_force_open_target) - self._page.linkHovered.connect(self.linkHovered) - self._page.mainFrame().loadStarted.connect(self.on_load_started) - self._page.change_title.connect(self.titleChanged) + page.linkHovered.connect(self.linkHovered) + page.mainFrame().loadStarted.connect(self.on_load_started) + page.change_title.connect(self.titleChanged) self.urlChanged.connect(self.on_url_changed) - self._page.mainFrame().loadFinished.connect(self.on_load_finished) + page.mainFrame().loadFinished.connect(self.on_load_finished) self.loadProgress.connect(lambda p: setattr(self, 'progress', p)) self.page().statusBarMessage.connect( lambda msg: setattr(self, 'statusbar_message', msg)) From 6a5c9ba138787984bbe91b8806793728861e48fb Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 20:43:03 +0200 Subject: [PATCH 56/79] Use object registry for CompletionView. --- qutebrowser/utils/completer.py | 34 +++++++++++++++++-------------- qutebrowser/widgets/completion.py | 3 ++- qutebrowser/widgets/mainwindow.py | 1 - 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/qutebrowser/utils/completer.py b/qutebrowser/utils/completer.py index 748d3d38d..2b8a1dc27 100644 --- a/qutebrowser/utils/completer.py +++ b/qutebrowser/utils/completer.py @@ -33,7 +33,6 @@ class Completer(QObject): """Completer which manages completions in a CompletionView. Attributes: - view: The CompletionView associated with this completer. ignore_change: Whether to ignore the next completion update. _models: dict of available completion models. @@ -48,9 +47,8 @@ class Completer(QObject): change_completed_part = pyqtSignal(str, bool) - def __init__(self, view): - super().__init__(view) - self.view = view + def __init__(self, parent=None): + super().__init__() self.ignore_change = False self._models = { @@ -63,6 +61,10 @@ class Completer(QObject): def __repr__(self): return '<{}>'.format(self.__class__.__name__) + def _model(self): + """Convienience method to get the current completion model.""" + return objreg.get('completion').model() + def _init_static_completions(self): """Initialize the static completion models.""" self._models[usertypes.Completion.command] = CFM( @@ -165,7 +167,7 @@ class Completer(QObject): indexes = selected.indexes() if not indexes: return - model = self.view.model() + model = self._model() data = model.data(indexes[0]) if data is None: return @@ -193,36 +195,38 @@ class Completer(QObject): log.completion.debug("Ignoring completion update") return + completion = objreg.get('completion') + if prefix != ':': # This is a search or gibberish, so we don't need to complete # anything (yet) # FIXME complete searchs - self.view.hide() + completion.hide() return model = self._get_new_completion(parts, cursor_part) - if model != self.view.model(): + if model != self._model(): if model is None: - self.view.hide() + completion.hide() else: - self.view.set_model(model) + completion.set_model(model) if model is None: log.completion.debug("No completion model for {}.".format(parts)) return pattern = parts[cursor_part] if parts else '' - self.view.model().set_pattern(pattern) + self._model().set_pattern(pattern) log.completion.debug( "New completion for {}: {}, with pattern '{}'".format( parts, model.srcmodel.__class__.__name__, pattern)) - if self.view.model().count() == 0: - self.view.hide() + if self._model().count() == 0: + completion.hide() return - self.view.model().mark_all_items(pattern) - if self.view.enabled: - self.view.show() + self._model().mark_all_items(pattern) + if completion.enabled: + completion.show() diff --git a/qutebrowser/widgets/completion.py b/qutebrowser/widgets/completion.py index 5a237a824..8a3b9a28a 100644 --- a/qutebrowser/widgets/completion.py +++ b/qutebrowser/widgets/completion.py @@ -91,7 +91,8 @@ class CompletionView(QTreeView): def __init__(self, parent=None): super().__init__(parent) - completer_obj = completer.Completer(self) + objreg.register('completion', self) + completer_obj = completer.Completer() objreg.register('completer', completer_obj) self.enabled = config.get('completion', 'show') diff --git a/qutebrowser/widgets/mainwindow.py b/qutebrowser/widgets/mainwindow.py index 3455a2e56..ddb15e002 100644 --- a/qutebrowser/widgets/mainwindow.py +++ b/qutebrowser/widgets/mainwindow.py @@ -87,7 +87,6 @@ class MainWindow(QWidget): self._vbox.addWidget(self._tabbed_browser) self._completion = completion.CompletionView(self) - objreg.register('completion', self._completion) self.status = bar.StatusBar() self._vbox.addWidget(self.status) From aa5e1922efbfc6187d9e588306b77c380eb386a2 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 20:50:14 +0200 Subject: [PATCH 57/79] Remove _view attribute in BrowserPage. --- qutebrowser/browser/webpage.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/qutebrowser/browser/webpage.py b/qutebrowser/browser/webpage.py index bdfef0848..100f14270 100644 --- a/qutebrowser/browser/webpage.py +++ b/qutebrowser/browser/webpage.py @@ -40,7 +40,6 @@ class BrowserPage(QWebPage): Attributes: _extension_handlers: Mapping of QWebPage extensions to their handlers. - _view: The QWebView associated with this page. _networkmnager: The NetworkManager used. Signals: @@ -51,8 +50,8 @@ class BrowserPage(QWebPage): start_download = pyqtSignal('QNetworkReply*') change_title = pyqtSignal(str) - def __init__(self, view): - super().__init__(view) + def __init__(self, parent=None): + super().__init__(parent) self._extension_handlers = { QWebPage.ErrorPageExtension: self._handle_errorpage, QWebPage.ChooseMultipleFilesExtension: self._handle_multiple_files, @@ -63,7 +62,6 @@ class BrowserPage(QWebPage): self.printRequested.connect(self.on_print_requested) self.downloadRequested.connect(self.on_download_requested) self.unsupportedContent.connect(self.on_unsupported_content) - self._view = view if PYQT_VERSION > 0x050300: # WORKAROUND (remove this when we bump the requirements to 5.3.1) @@ -300,10 +298,10 @@ class BrowserPage(QWebPage): message.error("Invalid link {} clicked!".format(urlstr)) log.webview.debug(url.errorString()) return False - if self._view.open_target == usertypes.ClickTarget.tab: + if self.view().open_target == usertypes.ClickTarget.tab: objreg.get('tabbed-browser').tabopen(url, False) return False - elif self._view.open_target == usertypes.ClickTarget.tab_bg: + elif self.view().open_target == usertypes.ClickTarget.tab_bg: objreg.get('tabbed-browser').tabopen(url, True) return False else: From 59058361b84de91bef1c6401fa070219e98b8242 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 20:50:22 +0200 Subject: [PATCH 58/79] Add missing objreg import in browser.webpage. --- qutebrowser/browser/webpage.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/browser/webpage.py b/qutebrowser/browser/webpage.py index 100f14270..c5d499adc 100644 --- a/qutebrowser/browser/webpage.py +++ b/qutebrowser/browser/webpage.py @@ -31,7 +31,7 @@ from PyQt5.QtWebKitWidgets import QWebPage from qutebrowser.config import config from qutebrowser.network import networkmanager from qutebrowser.utils import (message, usertypes, log, http, jinja, qtutils, - utils) + utils, objreg) class BrowserPage(QWebPage): From 8cb6ba01e3b180a0cbe029956578e0d3bbd33798 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 20:53:21 +0200 Subject: [PATCH 59/79] Use object registry for TabbedBrowser in SignalFilter. --- qutebrowser/browser/signalfilter.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/qutebrowser/browser/signalfilter.py b/qutebrowser/browser/signalfilter.py index 100f1716b..320f3f58f 100644 --- a/qutebrowser/browser/signalfilter.py +++ b/qutebrowser/browser/signalfilter.py @@ -23,7 +23,7 @@ import functools from PyQt5.QtCore import QObject -from qutebrowser.utils import debug, log +from qutebrowser.utils import debug, log, objreg from qutebrowser.widgets import webview @@ -34,19 +34,12 @@ class SignalFilter(QObject): Signals are only passed to the parent TabbedBrowser if they originated in the currently shown widget. - Attributes: - _tabs: The QTabWidget associated with this SignalFilter. - Class attributes: BLACKLIST: List of signal names which should not be logged. """ BLACKLIST = ['cur_scroll_perc_changed', 'cur_progress'] - def __init__(self, tabs): - super().__init__(tabs) - self._tabs = tabs - def create(self, signal, tab): """Factory for partial _filter_signals functions. @@ -80,12 +73,13 @@ class SignalFilter(QObject): The target signal if the sender was the current widget. """ log_signal = debug.signal_name(signal) not in self.BLACKLIST + tabbed_browser = objreg.get('tabbed-browser') try: - tabidx = self._tabs.indexOf(tab) + tabidx = tabbed_browser.indexOf(tab) except RuntimeError: # The tab has been deleted already return - if tabidx == self._tabs.currentIndex(): + if tabidx == tabbed_browser.currentIndex(): if log_signal: log.signals.debug("emitting: {} (tab {})".format( debug.dbg_signal(signal, args), tabidx)) From 507354c8d11d95784d616f39a1712b42b68db4f1 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 20:58:15 +0200 Subject: [PATCH 60/79] Use object registry for Prompt in Prompter. --- qutebrowser/widgets/statusbar/prompt.py | 1 + qutebrowser/widgets/statusbar/prompter.py | 69 ++++++++++++----------- 2 files changed, 37 insertions(+), 33 deletions(-) diff --git a/qutebrowser/widgets/statusbar/prompt.py b/qutebrowser/widgets/statusbar/prompt.py index 015cbd795..46df80498 100644 --- a/qutebrowser/widgets/statusbar/prompt.py +++ b/qutebrowser/widgets/statusbar/prompt.py @@ -55,6 +55,7 @@ class Prompt(QWidget): def __init__(self, parent=None): super().__init__(parent) + objreg.register('prompt', self) self._hbox = QHBoxLayout(self) self._hbox.setContentsMargins(0, 0, 0, 0) self._hbox.setSpacing(5) diff --git a/qutebrowser/widgets/statusbar/prompter.py b/qutebrowser/widgets/statusbar/prompter.py index 22b8874e6..62d2ebfb4 100644 --- a/qutebrowser/widgets/statusbar/prompter.py +++ b/qutebrowser/widgets/statusbar/prompter.py @@ -60,7 +60,6 @@ class Prompter: question: A Question object with the question to be asked to the user. _loops: A list of local EventLoops to spin in when blocking. _queue: A deque of waiting questions. - _prompt: The associated Prompt widget. """ def __init__(self, prompt): @@ -68,7 +67,6 @@ class Prompter: self._loops = [] self._queue = collections.deque() self._busy = False - self._prompt = prompt def __repr__(self): return '<{}>'.format(self.__class__.__name__) @@ -87,11 +85,12 @@ class Prompter: """Get a PromptContext based on the current state.""" if not self._busy: return None + prompt = objreg.get('prompt') ctx = PromptContext(question=self.question, - text=self._prompt.txt.text(), - input_text=self._prompt.lineedit.text(), - echo_mode=self._prompt.lineedit.echoMode(), - input_visible=self._prompt.lineedit.isVisible()) + text=prompt.txt.text(), + input_text=prompt.lineedit.text(), + echo_mode=prompt.lineedit.echoMode(), + input_visible=prompt.lineedit.isVisible()) return ctx def _restore_ctx(self, ctx): @@ -103,15 +102,16 @@ class Prompter: Return: True if a context was restored, False otherwise. """ log.statusbar.debug("Restoring context {}".format(ctx)) + prompt = objreg.get('prompt') if ctx is None: - self._prompt.hide_prompt.emit() + prompt.hide_prompt.emit() self._busy = False return False self.question = ctx.question - self._prompt.txt.setText(ctx.text) - self._prompt.lineedit.setText(ctx.input_text) - self._prompt.lineedit.setEchoMode(ctx.echo_mode) - self._prompt.lineedit.setVisible(ctx.input_visible) + prompt.txt.setText(ctx.text) + prompt.lineedit.setText(ctx.input_text) + prompt.lineedit.setEchoMode(ctx.echo_mode) + prompt.lineedit.setVisible(ctx.input_visible) return True def _display_question(self): @@ -123,6 +123,7 @@ class Prompter: Raise: ValueError if the set PromptMode is invalid. """ + prompt = objreg.get('prompt') if self.question.mode == usertypes.PromptMode.yesno: if self.question.default is None: suffix = "" @@ -130,29 +131,29 @@ class Prompter: suffix = " (yes)" else: suffix = " (no)" - self._prompt.txt.setText(self.question.text + suffix) - self._prompt.lineedit.hide() + prompt.txt.setText(self.question.text + suffix) + prompt.lineedit.hide() mode = usertypes.KeyMode.yesno elif self.question.mode == usertypes.PromptMode.text: - self._prompt.txt.setText(self.question.text) + prompt.txt.setText(self.question.text) if self.question.default: - self._prompt.lineedit.setText(self.question.default) - self._prompt.lineedit.show() + prompt.lineedit.setText(self.question.default) + prompt.lineedit.show() mode = usertypes.KeyMode.prompt elif self.question.mode == usertypes.PromptMode.user_pwd: - self._prompt.txt.setText(self.question.text) + prompt.txt.setText(self.question.text) if self.question.default: - self._prompt.lineedit.setText(self.question.default) - self._prompt.lineedit.show() + prompt.lineedit.setText(self.question.default) + prompt.lineedit.show() mode = usertypes.KeyMode.prompt elif self.question.mode == usertypes.PromptMode.alert: - self._prompt.txt.setText(self.question.text + ' (ok)') - self._prompt.lineedit.hide() + prompt.txt.setText(self.question.text + ' (ok)') + prompt.lineedit.hide() mode = usertypes.KeyMode.prompt else: raise ValueError("Invalid prompt mode!") - self._prompt.lineedit.setFocus() - self._prompt.show_prompt.emit() + prompt.lineedit.setFocus() + prompt.show_prompt.emit() self._busy = True return mode @@ -176,11 +177,12 @@ class Prompter: @pyqtSlot(usertypes.KeyMode) def on_mode_left(self, mode): """Clear and reset input when the mode was left.""" + prompt = objreg.get('prompt') if mode in (usertypes.KeyMode.prompt, usertypes.KeyMode.yesno): - self._prompt.txt.setText('') - self._prompt.lineedit.clear() - self._prompt.lineedit.setEchoMode(QLineEdit.Normal) - self._prompt.hide_prompt.emit() + prompt.txt.setText('') + prompt.lineedit.clear() + prompt.lineedit.setEchoMode(QLineEdit.Normal) + prompt.hide_prompt.emit() self._busy = False if self.question.answer is None and not self.question.is_aborted: self.question.cancel() @@ -196,22 +198,23 @@ class Prompter: This executes the next action depending on the question mode, e.g. asks for the password or leaves the mode. """ + prompt = objreg.get('prompt') if (self.question.mode == usertypes.PromptMode.user_pwd and self.question.user is None): # User just entered an username - self.question.user = self._prompt.lineedit.text() - self._prompt.txt.setText("Password:") - self._prompt.lineedit.clear() - self._prompt.lineedit.setEchoMode(QLineEdit.Password) + self.question.user = prompt.lineedit.text() + prompt.txt.setText("Password:") + prompt.lineedit.clear() + prompt.lineedit.setEchoMode(QLineEdit.Password) elif self.question.mode == usertypes.PromptMode.user_pwd: # User just entered a password - password = self._prompt.lineedit.text() + password = prompt.lineedit.text() self.question.answer = (self.question.user, password) modeman.leave(usertypes.KeyMode.prompt, 'prompt accept') self.question.done() elif self.question.mode == usertypes.PromptMode.text: # User just entered text. - self.question.answer = self._prompt.lineedit.text() + self.question.answer = prompt.lineedit.text() modeman.leave(usertypes.KeyMode.prompt, 'prompt accept') self.question.done() elif self.question.mode == usertypes.PromptMode.yesno: From 75da8a7f1bad1e4de607f58a9749cec460f826d1 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 21:00:05 +0200 Subject: [PATCH 61/79] Add missing parent=None --- qutebrowser/widgets/tabwidget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/widgets/tabwidget.py b/qutebrowser/widgets/tabwidget.py index a43c993ab..13368f05d 100644 --- a/qutebrowser/widgets/tabwidget.py +++ b/qutebrowser/widgets/tabwidget.py @@ -42,7 +42,7 @@ class TabWidget(QTabWidget): """The tabwidget used for TabbedBrowser.""" - def __init__(self, parent): + def __init__(self, parent=None): super().__init__(parent) bar = TabBar() self.setTabBar(bar) From 62e55499ebfc4008d27c29d5cdbd55dca153d15d Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 22:17:53 +0200 Subject: [PATCH 62/79] Privatize all attributes which aren't needed from the outside. --- qutebrowser/browser/downloads.py | 132 +++++++++--------- qutebrowser/commands/cmdutils.py | 57 ++++---- qutebrowser/commands/command.py | 63 +++++---- qutebrowser/commands/userscripts.py | 82 +++++------ qutebrowser/config/configtypes.py | 60 ++++---- qutebrowser/keyinput/basekeyparser.py | 8 +- qutebrowser/keyinput/keyparser.py | 10 +- qutebrowser/test/utils/test_editor.py | 24 ++-- qutebrowser/test/utils/test_log.py | 12 +- qutebrowser/test/utils/test_readline.py | 10 +- .../test/utils/usertypes/test_neighborlist.py | 90 ++++++------ qutebrowser/utils/completer.py | 12 +- qutebrowser/utils/docutils.py | 30 ++-- qutebrowser/utils/editor.py | 32 ++--- qutebrowser/utils/jinja.py | 4 +- qutebrowser/utils/log.py | 18 +-- qutebrowser/utils/readline.py | 16 +-- qutebrowser/utils/usertypes.py | 30 ++-- qutebrowser/utils/utils.py | 12 +- qutebrowser/widgets/console.py | 38 ++--- qutebrowser/widgets/mainwindow.py | 10 +- qutebrowser/widgets/statusbar/command.py | 18 +-- qutebrowser/widgets/statusbar/prompter.py | 86 ++++++------ 23 files changed, 430 insertions(+), 424 deletions(-) diff --git a/qutebrowser/browser/downloads.py b/qutebrowser/browser/downloads.py index ea80c85bb..498eb9592 100644 --- a/qutebrowser/browser/downloads.py +++ b/qutebrowser/browser/downloads.py @@ -43,15 +43,15 @@ class DownloadItem(QObject): estimate the remaining time. Attributes: - reply: The QNetworkReply associated with this download. - bytes_done: How many bytes there are already downloaded. - bytes_total: The total count of bytes. - None if the total is unknown. - speed: The current download speed, in bytes per second. - fileobj: The file object to download the file to. - filename: The filename of the download. - is_cancelled: Whether the download was cancelled. - speed_avg: A rolling average of speeds. + _bytes_done: How many bytes there are already downloaded. + _bytes_total: The total count of bytes. + None if the total is unknown. + _speed: The current download speed, in bytes per second. + _fileobj: The file object to download the file to. + _filename: The filename of the download. + _is_cancelled: Whether the download was cancelled. + _speed_avg: A rolling average of speeds. + _reply: The QNetworkReply associated with this download. _last_done: The count of bytes which where downloaded when calculating the speed the last time. @@ -77,18 +77,18 @@ class DownloadItem(QObject): reply: The QNetworkReply to download. """ super().__init__(parent) - self.reply = reply - self.bytes_total = None - self.speed = 0 + self._reply = reply + self._bytes_total = None + self._speed = 0 self.basename = '???' samples = int(self.SPEED_AVG_WINDOW * (1000 / self.SPEED_REFRESH_INTERVAL)) - self.speed_avg = collections.deque(maxlen=samples) - self.fileobj = None - self.filename = None - self.is_cancelled = False + self._speed_avg = collections.deque(maxlen=samples) + self._fileobj = None + self._filename = None + self._is_cancelled = False self._do_delayed_write = False - self.bytes_done = 0 + self._bytes_done = 0 self._last_done = 0 reply.setReadBufferSize(16 * 1024 * 1024) reply.downloadProgress.connect(self.on_download_progress) @@ -115,11 +115,11 @@ class DownloadItem(QObject): Example: foo.pdf [699.2kB/s|0.34|16%|4.253/25.124] """ - speed = utils.format_size(self.speed, suffix='B/s') - down = utils.format_size(self.bytes_done, suffix='B') + speed = utils.format_size(self._speed, suffix='B/s') + down = utils.format_size(self._bytes_done, suffix='B') perc = self._percentage() remaining = self._remaining_time() - if all(e is None for e in (perc, remaining, self.bytes_total)): + if all(e is None for e in (perc, remaining, self._bytes_total)): return ('{name} [{speed:>10}|{down}]'.format( name=self.basename, speed=speed, down=down)) if perc is None: @@ -130,7 +130,7 @@ class DownloadItem(QObject): remaining = '?' else: remaining = utils.format_seconds(remaining) - total = utils.format_size(self.bytes_total, suffix='B') + total = utils.format_size(self._bytes_total, suffix='B') return ('{name} [{speed:>10}|{remaining:>5}|{perc:>2}%|' '{down}/{total}]'.format(name=self.basename, speed=speed, remaining=remaining, perc=perc, @@ -138,36 +138,36 @@ class DownloadItem(QObject): def _die(self, msg): """Abort the download and emit an error.""" - self.reply.downloadProgress.disconnect() - self.reply.finished.disconnect() - self.reply.error.disconnect() - self.reply.readyRead.disconnect() - self.bytes_done = self.bytes_total + self._reply.downloadProgress.disconnect() + self._reply.finished.disconnect() + self._reply.error.disconnect() + self._reply.readyRead.disconnect() + self._bytes_done = self._bytes_total self.timer.stop() self.error.emit(msg) - self.reply.abort() - self.reply.deleteLater() - if self.fileobj is not None: + self._reply.abort() + self._reply.deleteLater() + if self._fileobj is not None: try: - self.fileobj.close() + self._fileobj.close() except OSError as e: self.error.emit(e.strerror) self.finished.emit() def _percentage(self): """The current download percentage, or None if unknown.""" - if self.bytes_total == 0 or self.bytes_total is None: + if self._bytes_total == 0 or self._bytes_total is None: return None else: - return 100 * self.bytes_done / self.bytes_total + return 100 * self._bytes_done / self._bytes_total def _remaining_time(self): """The remaining download time in seconds, or None.""" - if self.bytes_total is None or not self.speed_avg: + if self._bytes_total is None or not self._speed_avg: # No average yet or we don't know the total size. return None - remaining_bytes = self.bytes_total - self.bytes_done - avg = sum(self.speed_avg) / len(self.speed_avg) + remaining_bytes = self._bytes_total - self._bytes_done + avg = sum(self._speed_avg) / len(self._speed_avg) if avg == 0: # Download stalled return None @@ -189,13 +189,13 @@ class DownloadItem(QObject): """Cancel the download.""" log.downloads.debug("cancelled") self.cancelled.emit() - self.is_cancelled = True - self.reply.abort() - self.reply.deleteLater() - if self.fileobj is not None: - self.fileobj.close() - if self.filename is not None and os.path.exists(self.filename): - os.remove(self.filename) + self._is_cancelled = True + self._reply.abort() + self._reply.deleteLater() + if self._fileobj is not None: + self._fileobj.close() + if self._filename is not None and os.path.exists(self._filename): + os.remove(self._filename) self.finished.emit() def set_filename(self, filename): @@ -205,19 +205,19 @@ class DownloadItem(QObject): filename: The full filename to save the download to. None: special value to stop the download. """ - if self.filename is not None: + if self._filename is not None: raise ValueError("Filename was already set! filename: {}, " - "existing: {}".format(filename, self.filename)) + "existing: {}".format(filename, self._filename)) filename = os.path.expanduser(filename) if os.path.isabs(filename) and os.path.isdir(filename): # We got an absolute directory from the user, so we save it under # the default filename in that directory. - self.filename = os.path.join(filename, self.basename) + self._filename = os.path.join(filename, self.basename) elif os.path.isabs(filename): # We got an absolute filename from the user, so we save it under # that filename. - self.filename = filename - self.basename = os.path.basename(self.filename) + self._filename = filename + self.basename = os.path.basename(self._filename) else: # We only got a filename (without directory) from the user, so we # save it under that filename in the default directory. @@ -225,11 +225,11 @@ class DownloadItem(QObject): if download_dir is None: download_dir = utils.get_standard_dir( QStandardPaths.DownloadLocation) - self.filename = os.path.join(download_dir, filename) + self._filename = os.path.join(download_dir, filename) self.basename = filename log.downloads.debug("Setting filename to {}".format(filename)) try: - self.fileobj = open(self.filename, 'wb') + self._fileobj = open(self._filename, 'wb') if self._do_delayed_write: # Downloading to the buffer in RAM has already finished so we # write out the data and clean up now. @@ -246,10 +246,10 @@ class DownloadItem(QObject): """Write buffered data to disk and finish the QNetworkReply.""" log.downloads.debug("Doing delayed write...") self._do_delayed_write = False - self.fileobj.write(self.reply.readAll()) - self.fileobj.close() - self.reply.close() - self.reply.deleteLater() + self._fileobj.write(self._reply.readAll()) + self._fileobj.close() + self._reply.close() + self._reply.deleteLater() self.finished.emit() log.downloads.debug("Download finished") @@ -263,8 +263,8 @@ class DownloadItem(QObject): """ if bytes_total == -1: bytes_total = None - self.bytes_done = bytes_done - self.bytes_total = bytes_total + self._bytes_done = bytes_done + self._bytes_total = bytes_total self.data_changed.emit() @pyqtSlot() @@ -275,12 +275,12 @@ class DownloadItem(QObject): doesn't mean the download (i.e. writing data to the disk) is finished as well. Therefore, we can't close() the QNetworkReply in here yet. """ - self.bytes_done = self.bytes_total + self._bytes_done = self._bytes_total self.timer.stop() - if self.is_cancelled: + if self._is_cancelled: return - log.downloads.debug("Reply finished, fileobj {}".format(self.fileobj)) - if self.fileobj is None: + log.downloads.debug("Reply finished, fileobj {}".format(self._fileobj)) + if self._fileobj is None: # We'll handle emptying the buffer and cleaning up as soon as the # filename is set. self._do_delayed_write = True @@ -292,11 +292,11 @@ class DownloadItem(QObject): @pyqtSlot() def on_ready_read(self): """Read available data and save file when ready to read.""" - if self.fileobj is None: + if self._fileobj is None: # No filename has been set yet, so we don't empty the buffer. return try: - self.fileobj.write(self.reply.readAll()) + self._fileobj.write(self._reply.readAll()) except OSError as e: self._die(e.strerror) @@ -306,15 +306,15 @@ class DownloadItem(QObject): if code == QNetworkReply.OperationCanceledError: return else: - self.error.emit(self.reply.errorString()) + self.error.emit(self._reply.errorString()) @pyqtSlot() def update_speed(self): """Recalculate the current download speed.""" - delta = self.bytes_done - self._last_done - self.speed = delta * 1000 / self.SPEED_REFRESH_INTERVAL - self.speed_avg.append(self.speed) - self._last_done = self.bytes_done + delta = self._bytes_done - self._last_done + self._speed = delta * 1000 / self.SPEED_REFRESH_INTERVAL + self._speed_avg.append(self._speed) + self._last_done = self._bytes_done self.data_changed.emit() diff --git a/qutebrowser/commands/cmdutils.py b/qutebrowser/commands/cmdutils.py index d7707d26e..6f499f85d 100644 --- a/qutebrowser/commands/cmdutils.py +++ b/qutebrowser/commands/cmdutils.py @@ -91,16 +91,16 @@ class register: # pylint: disable=invalid-name much cleaner to implement. Attributes: - instance: The instance to be used as "self", as a dotted string. - name: The name (as string) or names (as list) of the command. - split: Whether to split the arguments. - hide: Whether to hide the command or not. - completion: Which completion to use for arguments, as a list of - strings. - modes/not_modes: List of modes to use/not use. - needs_js: If javascript is needed for this command. - debug: Whether this is a debugging command (only shown with --debug). - ignore_args: Whether to ignore the arguments of the function. + _instance: The instance to be used as "self", as a dotted string. + _name: The name (as string) or names (as list) of the command. + _split: Whether to split the arguments. + _hide: Whether to hide the command or not. + _completion: Which completion to use for arguments, as a list of + strings. + _modes/_not_modes: List of modes to use/not use. + _needs_js: If javascript is needed for this command. + _debug: Whether this is a debugging command (only shown with --debug). + _ignore_args: Whether to ignore the arguments of the function. """ def __init__(self, instance=None, name=None, split=True, hide=False, @@ -116,16 +116,16 @@ class register: # pylint: disable=invalid-name # pylint: disable=too-many-arguments if modes is not None and not_modes is not None: raise ValueError("Only modes or not_modes can be given!") - self.name = name - self.split = split - self.hide = hide - self.instance = instance - self.completion = completion - self.modes = modes - self.not_modes = not_modes - self.needs_js = needs_js - self.debug = debug - self.ignore_args = ignore_args + self._name = name + self._split = split + self._hide = hide + self._instance = instance + self._completion = completion + self._modes = modes + self._not_modes = not_modes + self._needs_js = needs_js + self._debug = debug + self._ignore_args = ignore_args if modes is not None: for m in modes: if not isinstance(m, usertypes.KeyMode): @@ -150,12 +150,12 @@ class register: # pylint: disable=invalid-name Return: A list of names, with the main name being the first item. """ - if self.name is None: + if self._name is None: return [func.__name__.lower().replace('_', '-')] - elif isinstance(self.name, str): - return [self.name] + elif isinstance(self._name, str): + return [self._name] else: - return self.name + return self._name def __call__(self, func): """Register the command before running the function. @@ -178,10 +178,11 @@ class register: # pylint: disable=invalid-name if name in cmd_dict: raise ValueError("{} is already registered!".format(name)) cmd = command.Command( - name=names[0], split=self.split, hide=self.hide, - instance=self.instance, completion=self.completion, - modes=self.modes, not_modes=self.not_modes, needs_js=self.needs_js, - is_debug=self.debug, ignore_args=self.ignore_args, handler=func) + name=names[0], split=self._split, hide=self._hide, + instance=self._instance, completion=self._completion, + modes=self._modes, not_modes=self._not_modes, + needs_js=self._needs_js, is_debug=self._debug, + ignore_args=self._ignore_args, handler=func) for name in names: cmd_dict[name] = cmd aliases += names[1:] diff --git a/qutebrowser/commands/command.py b/qutebrowser/commands/command.py index b2dd39810..bcb77b907 100644 --- a/qutebrowser/commands/command.py +++ b/qutebrowser/commands/command.py @@ -37,17 +37,18 @@ class Command: name: The main name of the command. split: Whether to split the arguments. hide: Whether to hide the arguments or not. - count: Whether the command supports a count, or not. desc: The description of the command. - instance: How to get to the "self" argument of the handler. - A dotted string as viewed from app.py, or None. handler: The handler function to call. completion: Completions to use for arguments, as a list of strings. - needs_js: Whether the command needs javascript enabled debug: Whether this is a debugging command (only shown with --debug). parser: The ArgumentParser to use to parse this command. - type_conv: A mapping of conversion functions for arguments. - name_conv: A mapping of argument names to parameter names. + _type_conv: A mapping of conversion functions for arguments. + _name_conv: A mapping of argument names to parameter names. + _needs_js: Whether the command needs javascript enabled + _modes: The modes the command can be executed in. + _not_modes: The modes the command can not be executed in. + _count: Whether the command supports a count, or not. + _instance: The object to bind 'self' to. Class attributes: AnnotationInfo: Named tuple for info from an annotation. @@ -66,11 +67,11 @@ class Command: self.name = name self.split = split self.hide = hide - self.instance = instance + self._instance = instance self.completion = completion - self.modes = modes - self.not_modes = not_modes - self.needs_js = needs_js + self._modes = modes + self._not_modes = not_modes + self._needs_js = needs_js self.debug = is_debug self.ignore_args = ignore_args self.handler = handler @@ -84,13 +85,13 @@ class Command: self._check_func() self.opt_args = collections.OrderedDict() self.namespace = None - self.count = None + self._count = None self.pos_args = [] has_count, desc, type_conv, name_conv = self._inspect_func() self.has_count = has_count self.desc = desc - self.type_conv = type_conv - self.name_conv = name_conv + self._type_conv = type_conv + self._name_conv = name_conv def _check_prerequisites(self): """Check if the command is permitted to run currently. @@ -99,17 +100,17 @@ class Command: PrerequisitesError if the command can't be called currently. """ curmode = objreg.get('mode-manager').mode() - if self.modes is not None and curmode not in self.modes: - mode_names = '/'.join(mode.name for mode in self.modes) + if self._modes is not None and curmode not in self._modes: + mode_names = '/'.join(mode.name for mode in self._modes) raise cmdexc.PrerequisitesError( "{}: This command is only allowed in {} mode.".format( self.name, mode_names)) - elif self.not_modes is not None and curmode in self.not_modes: - mode_names = '/'.join(mode.name for mode in self.not_modes) + elif self._not_modes is not None and curmode in self._not_modes: + mode_names = '/'.join(mode.name for mode in self._not_modes) raise cmdexc.PrerequisitesError( "{}: This command is not allowed in {} mode.".format( self.name, mode_names)) - if self.needs_js and not QWebSettings.globalSettings().testAttribute( + if self._needs_js and not QWebSettings.globalSettings().testAttribute( QWebSettings.JavascriptEnabled): raise cmdexc.PrerequisitesError( "{}: This command needs javascript enabled.".format(self.name)) @@ -117,10 +118,10 @@ class Command: def _check_func(self): """Make sure the function parameters don't violate any rules.""" signature = inspect.signature(self.handler) - if 'self' in signature.parameters and self.instance is None: + if 'self' in signature.parameters and self._instance is None: raise TypeError("{} is a class method, but instance was not " "given!".format(self.name[0])) - elif 'self' not in signature.parameters and self.instance is not None: + elif 'self' not in signature.parameters and self._instance is not None: raise TypeError("{} is not a class method, but instance was " "given!".format(self.name[0])) elif inspect.getfullargspec(self.handler).varkw is not None: @@ -299,7 +300,7 @@ class Command: args: The positional argument list. Gets modified directly. """ assert param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD - obj = objreg.get(self.instance) + obj = objreg.get(self._instance) args.append(obj) def _get_count_arg(self, param, args, kwargs): @@ -314,27 +315,27 @@ class Command: raise TypeError("{}: count argument given with a command which " "does not support count!".format(self.name)) if param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD: - if self.count is not None: - args.append(self.count) + if self._count is not None: + args.append(self._count) else: args.append(param.default) elif param.kind == inspect.Parameter.KEYWORD_ONLY: - if self.count is not None: - kwargs['count'] = self.count + if self._count is not None: + kwargs['count'] = self._count else: raise TypeError("{}: invalid parameter type {} for argument " "'count'!".format(self.name, param.kind)) def _get_param_name_and_value(self, param): """Get the converted name and value for an inspect.Parameter.""" - name = self.name_conv.get(param.name, param.name) + name = self._name_conv.get(param.name, param.name) value = getattr(self.namespace, name) - if param.name in self.type_conv: + if param.name in self._type_conv: # We convert enum types after getting the values from # argparse, because argparse's choices argument is # processed after type conversation, which is not what we # want. - value = self.type_conv[param.name](value) + value = self._type_conv[param.name](value) return name, value def _get_call_args(self): @@ -349,13 +350,13 @@ class Command: signature = inspect.signature(self.handler) if self.ignore_args: - if self.instance is not None: + if self._instance is not None: param = list(signature.parameters.values())[0] self._get_self_arg(param, args) return args, kwargs for i, param in enumerate(signature.parameters.values()): - if i == 0 and self.instance is not None: + if i == 0 and self._instance is not None: # Special case for 'self'. self._get_self_arg(param, args) continue @@ -401,7 +402,7 @@ class Command: log.commands.debug("argparser exited with status {}: {}".format( e.status, e)) return - self.count = count + self._count = count posargs, kwargs = self._get_call_args() self._check_prerequisites() log.commands.debug('Calling {}'.format( diff --git a/qutebrowser/commands/userscripts.py b/qutebrowser/commands/userscripts.py index b831ca00f..f412ec323 100644 --- a/qutebrowser/commands/userscripts.py +++ b/qutebrowser/commands/userscripts.py @@ -51,7 +51,7 @@ class _BlockingFIFOReader(QObject): was requested. Attributes: - filepath: The filename of the FIFO to read. + _filepath: The filename of the FIFO to read. fifo: The file object which is being read. Signals: @@ -65,7 +65,7 @@ class _BlockingFIFOReader(QObject): def __init__(self, filepath, parent=None): super().__init__(parent) - self.filepath = filepath + self._filepath = filepath self.fifo = None def read(self): @@ -74,7 +74,7 @@ class _BlockingFIFOReader(QObject): # See http://www.outflux.net/blog/archives/2008/03/09/using-select-on-a-fifo/ # We also use os.open and os.fdopen rather than built-in open so we can # add O_NONBLOCK. - fd = os.open(self.filepath, os.O_RDWR | + fd = os.open(self._filepath, os.O_RDWR | os.O_NONBLOCK, # pylint: disable=no-member encoding='utf-8') self.fifo = os.fdopen(fd, 'r') @@ -96,8 +96,8 @@ class _BaseUserscriptRunner(QObject): """Common part between the Windows and the POSIX userscript runners. Attributes: - filepath: The path of the file/FIFO which is being read. - proc: The QProcess which is being executed. + _filepath: The path of the file/FIFO which is being read. + _proc: The QProcess which is being executed. Class attributes: PROCESS_MESSAGES: A mapping of QProcess::ProcessError members to @@ -124,8 +124,8 @@ class _BaseUserscriptRunner(QObject): def __init__(self, parent=None): super().__init__(parent) - self.filepath = None - self.proc = None + self._filepath = None + self._proc = None def _run_process(self, cmd, *args, env): """Start the given command via QProcess. @@ -135,27 +135,27 @@ class _BaseUserscriptRunner(QObject): *args: The arguments to hand to the command env: A dictionary of environment variables to add. """ - self.proc = QProcess(self) + self._proc = QProcess(self) procenv = QProcessEnvironment.systemEnvironment() - procenv.insert('QUTE_FIFO', self.filepath) + procenv.insert('QUTE_FIFO', self._filepath) if env is not None: for k, v in env.items(): procenv.insert(k, v) - self.proc.setProcessEnvironment(procenv) - self.proc.error.connect(self.on_proc_error) - self.proc.finished.connect(self.on_proc_finished) - self.proc.start(cmd, args) + self._proc.setProcessEnvironment(procenv) + self._proc.error.connect(self.on_proc_error) + self._proc.finished.connect(self.on_proc_finished) + self._proc.start(cmd, args) def _cleanup(self): """Clean up the temporary file.""" try: - os.remove(self.filepath) + os.remove(self._filepath) except PermissionError as e: # NOTE: Do not replace this with "raise CommandError" as it's # executed async. message.error("Failed to delete tempfile... ({})".format(e)) - self.filepath = None - self.proc = None + self._filepath = None + self._proc = None def run(self, cmd, *args, env=None): """Run the userscript given. @@ -192,14 +192,14 @@ class _POSIXUserscriptRunner(_BaseUserscriptRunner): executed immediately when they arrive in the FIFO. Attributes: - reader: The _BlockingFIFOReader instance. - thread: The QThread where reader runs. + _reader: The _BlockingFIFOReader instance. + _thread: The QThread where reader runs. """ def __init__(self, parent=None): super().__init__(parent) - self.reader = None - self.thread = None + self._reader = None + self._thread = None def run(self, cmd, *args, env=None): rundir = utils.get_standard_dir(QStandardPaths.RuntimeLocation) @@ -208,43 +208,43 @@ class _POSIXUserscriptRunner(_BaseUserscriptRunner): # directory and place the FIFO there, which sucks. Since os.kfifo will # raise an exception anyways when the path doesn't exist, it shouldn't # be a big issue. - self.filepath = tempfile.mktemp(prefix='userscript-', dir=rundir) - os.mkfifo(self.filepath) # pylint: disable=no-member + self._filepath = tempfile.mktemp(prefix='userscript-', dir=rundir) + os.mkfifo(self._filepath) # pylint: disable=no-member - self.reader = _BlockingFIFOReader(self.filepath) - self.thread = QThread(self) - self.reader.moveToThread(self.thread) - self.reader.got_line.connect(self.got_cmd) - self.thread.started.connect(self.reader.read) - self.reader.finished.connect(self.on_reader_finished) - self.thread.finished.connect(self.on_thread_finished) + self._reader = _BlockingFIFOReader(self._filepath) + self._thread = QThread(self) + self._reader.moveToThread(self._thread) + self._reader.got_line.connect(self.got_cmd) + self._thread.started.connect(self._reader.read) + self._reader.finished.connect(self.on_reader_finished) + self._thread.finished.connect(self.on_thread_finished) self._run_process(cmd, *args, env=env) - self.thread.start() + self._thread.start() def on_proc_finished(self): """Interrupt the reader when the process finished.""" log.procs.debug("proc finished") - self.thread.requestInterruption() + self._thread.requestInterruption() def on_proc_error(self, error): """Interrupt the reader when the process had an error.""" super().on_proc_error(error) - self.thread.requestInterruption() + self._thread.requestInterruption() def on_reader_finished(self): """Quit the thread and clean up when the reader finished.""" log.procs.debug("reader finished") - self.thread.quit() - self.reader.fifo.close() - self.reader.deleteLater() + self._thread.quit() + self._reader.fifo.close() + self._reader.deleteLater() super()._cleanup() self.finished.emit() def on_thread_finished(self): """Clean up the QThread object when the thread finished.""" log.procs.debug("thread finished") - self.thread.deleteLater() + self._thread.deleteLater() class _WindowsUserscriptRunner(_BaseUserscriptRunner): @@ -262,13 +262,13 @@ class _WindowsUserscriptRunner(_BaseUserscriptRunner): def __init__(self, parent=None): super().__init__(parent) - self.oshandle = None + self._oshandle = None def _cleanup(self): """Clean up temporary files after the userscript finished.""" - os.close(self.oshandle) + os.close(self._oshandle) super()._cleanup() - self.oshandle = None + self._oshandle = None def on_proc_finished(self): """Read back the commands when the process finished. @@ -277,7 +277,7 @@ class _WindowsUserscriptRunner(_BaseUserscriptRunner): got_cmd: Emitted for every command in the file. """ log.procs.debug("proc finished") - with open(self.filepath, 'r', encoding='utf-8') as f: + with open(self._filepath, 'r', encoding='utf-8') as f: for line in f: self.got_cmd.emit(line.rstrip()) self._cleanup() @@ -290,7 +290,7 @@ class _WindowsUserscriptRunner(_BaseUserscriptRunner): self.finished.emit() def run(self, cmd, *args, env=None): - self.oshandle, self.filepath = tempfile.mkstemp(text=True) + self._oshandle, self._filepath = tempfile.mkstemp(text=True) self._run_process(cmd, *args, env=env) diff --git a/qutebrowser/config/configtypes.py b/qutebrowser/config/configtypes.py index 32c682230..719a7439a 100644 --- a/qutebrowser/config/configtypes.py +++ b/qutebrowser/config/configtypes.py @@ -86,7 +86,7 @@ class BaseType: """A type used for a setting value. Attributes: - none_ok: Whether to convert to None for an empty string. + _none_ok: Whether to convert to None for an empty string. Class attributes: valid_values: Possible values if they can be expressed as a fixed @@ -98,7 +98,7 @@ class BaseType: valid_values = None def __init__(self, none_ok=False): - self.none_ok = none_ok + self._none_ok = none_ok def transform(self, value): """Transform the setting value. @@ -132,7 +132,7 @@ class BaseType: NotImplementedError if self.valid_values is not defined and this method should be overridden. """ - if not value and self.none_ok: + if not value and self._none_ok: return if self.valid_values is not None: if value not in self.valid_values: @@ -196,7 +196,7 @@ class String(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -224,7 +224,7 @@ class List(BaseType): def validate(self, value): vals = self.transform(value) if None in vals: - if self.none_ok: + if self._none_ok: pass else: raise ValidationError(value, "items may not be empty!") @@ -252,7 +252,7 @@ class Bool(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -290,7 +290,7 @@ class Int(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -321,7 +321,7 @@ class IntList(List): vals = self.transform(value) except ValueError: raise ValidationError(value, "must be a list of integers!") - if None in vals and not self.none_ok: + if None in vals and not self._none_ok: raise ValidationError(value, "items may not be empty!") @@ -352,7 +352,7 @@ class Float(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -395,7 +395,7 @@ class Perc(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty") @@ -442,7 +442,7 @@ class PercList(List): try: for val in vals: if val is None: - if self.none_ok: + if self._none_ok: continue else: raise ValidationError(value, "items may not be empty!") @@ -481,7 +481,7 @@ class PercOrInt(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -517,7 +517,7 @@ class Command(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -541,7 +541,7 @@ class ColorSystem(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -567,7 +567,7 @@ class QtColor(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -591,7 +591,7 @@ class CssColor(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -626,7 +626,7 @@ class QssColor(CssColor): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -662,7 +662,7 @@ class Font(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -729,7 +729,7 @@ class Regex(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -766,7 +766,7 @@ class RegexList(List): except sre_constants.error as e: raise ValidationError(value, "must be a list valid regexes - " + str(e)) - if not self.none_ok and None in vals: + if not self._none_ok and None in vals: raise ValidationError(value, "items may not be empty!") @@ -778,7 +778,7 @@ class File(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -802,7 +802,7 @@ class Directory(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -903,7 +903,7 @@ class WebKitBytesList(List): vals = super().transform(value) for val in vals: self.bytestype.validate(val) - if None in vals and not self.none_ok: + if None in vals and not self._none_ok: raise ValidationError(value, "items may not be empty!") if self.length is not None and len(vals) != self.length: raise ValidationError(value, "exactly {} values need to be " @@ -926,7 +926,7 @@ class ShellCommand(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -975,7 +975,7 @@ class Proxy(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -1022,7 +1022,7 @@ class SearchEngineName(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -1034,7 +1034,7 @@ class SearchEngineUrl(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -1054,7 +1054,7 @@ class Encoding(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -1075,7 +1075,7 @@ class UserStyleSheet(File): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") @@ -1117,7 +1117,7 @@ class AutoSearch(BaseType): def validate(self, value): if not value: - if self.none_ok: + if self._none_ok: return else: raise ValidationError(value, "may not be empty!") diff --git a/qutebrowser/keyinput/basekeyparser.py b/qutebrowser/keyinput/basekeyparser.py index e5be993f8..77bc61b98 100644 --- a/qutebrowser/keyinput/basekeyparser.py +++ b/qutebrowser/keyinput/basekeyparser.py @@ -53,8 +53,8 @@ class BaseKeyParser(QObject): Attributes: bindings: Bound keybindings special_bindings: Bound special bindings (). - warn_on_keychains: Whether a warning should be logged when binding - keychains in a section which does not support them. + _warn_on_keychains: Whether a warning should be logged when binding + keychains in a section which does not support them. _keystring: The currently entered key sequence _timer: Timer for delayed execution. _modename: The name of the input mode associated with this keyparser. @@ -83,7 +83,7 @@ class BaseKeyParser(QObject): supports_count = supports_chains self._supports_count = supports_count self._supports_chains = supports_chains - self.warn_on_keychains = True + self._warn_on_keychains = True self.bindings = {} self.special_bindings = {} @@ -330,7 +330,7 @@ class BaseKeyParser(QObject): self.special_bindings[keystr] = cmd elif self._supports_chains: self.bindings[key] = cmd - elif self.warn_on_keychains: + elif self._warn_on_keychains: log.keyboard.warning( "Ignoring keychain '{}' in mode '{}' because " "keychains are not supported there.".format(key, modename)) diff --git a/qutebrowser/keyinput/keyparser.py b/qutebrowser/keyinput/keyparser.py index 8529f459c..20b8cf966 100644 --- a/qutebrowser/keyinput/keyparser.py +++ b/qutebrowser/keyinput/keyparser.py @@ -29,17 +29,17 @@ class CommandKeyParser(BaseKeyParser): """KeyChainParser for command bindings. Attributes: - commandrunner: CommandRunner instance. + _commandrunner: CommandRunner instance. """ def __init__(self, parent=None, supports_count=None, supports_chains=False): super().__init__(parent, supports_count, supports_chains) - self.commandrunner = runners.CommandRunner() + self._commandrunner = runners.CommandRunner() def execute(self, cmdstr, _keytype, count=None): try: - self.commandrunner.run(cmdstr, count) + self._commandrunner.run(cmdstr, count) except (cmdexc.CommandMetaError, cmdexc.CommandError) as e: message.error(e, immediately=True) @@ -66,10 +66,10 @@ class PassthroughKeyParser(CommandKeyParser): """ super().__init__(parent, supports_chains=False) self.log = False - self.warn_on_keychains = warn + self._warn_on_keychains = warn self.read_config(mode) self._mode = mode def __repr__(self): return '<{} mode={}, warn={})'.format( - self.__class__.__name__, self._mode, self.warn_on_keychains) + self.__class__.__name__, self._mode, self._warn_on_keychains) diff --git a/qutebrowser/test/utils/test_editor.py b/qutebrowser/test/utils/test_editor.py index db63a5948..886af719e 100644 --- a/qutebrowser/test/utils/test_editor.py +++ b/qutebrowser/test/utils/test_editor.py @@ -19,6 +19,8 @@ """Tests for qutebrowser.utils.editor.""" +# pylint: disable=protected-access + import os import os.path import unittest @@ -59,7 +61,7 @@ class ArgTests(unittest.TestCase): editor.config = stubs.ConfigStub( {'general': {'editor': ['bin'], 'editor-encoding': 'utf-8'}}) self.editor.edit("") - self.editor.proc.start.assert_called_with("bin", []) + self.editor._proc.start.assert_called_with("bin", []) def test_start_args(self): """Test starting editor with static arguments.""" @@ -67,7 +69,7 @@ class ArgTests(unittest.TestCase): {'general': {'editor': ['bin', 'foo', 'bar'], 'editor-encoding': 'utf-8'}}) self.editor.edit("") - self.editor.proc.start.assert_called_with("bin", ["foo", "bar"]) + self.editor._proc.start.assert_called_with("bin", ["foo", "bar"]) def test_placeholder(self): """Test starting editor with placeholder argument.""" @@ -75,9 +77,9 @@ class ArgTests(unittest.TestCase): {'general': {'editor': ['bin', 'foo', '{}', 'bar'], 'editor-encoding': 'utf-8'}}) self.editor.edit("") - filename = self.editor.filename - self.editor.proc.start.assert_called_with("bin", - ["foo", filename, "bar"]) + filename = self.editor._filename + self.editor._proc.start.assert_called_with("bin", + ["foo", filename, "bar"]) def test_in_arg_placeholder(self): """Test starting editor with placeholder argument inside argument.""" @@ -85,7 +87,7 @@ class ArgTests(unittest.TestCase): {'general': {'editor': ['bin', 'foo{}bar'], 'editor-encoding': 'utf-8'}}) self.editor.edit("") - self.editor.proc.start.assert_called_with("bin", ["foo{}bar"]) + self.editor._proc.start.assert_called_with("bin", ["foo{}bar"]) def tearDown(self): self.editor._cleanup() # pylint: disable=protected-access @@ -107,7 +109,7 @@ class FileHandlingTests(unittest.TestCase): def test_file_handling_closed_ok(self): """Test file handling when closing with an exitstatus == 0.""" self.editor.edit("") - filename = self.editor.filename + filename = self.editor._filename self.assertTrue(os.path.exists(filename)) self.editor.on_proc_closed(0, QProcess.NormalExit) self.assertFalse(os.path.exists(filename)) @@ -115,7 +117,7 @@ class FileHandlingTests(unittest.TestCase): def test_file_handling_closed_error(self): """Test file handling when closing with an exitstatus != 0.""" self.editor.edit("") - filename = self.editor.filename + filename = self.editor._filename self.assertTrue(os.path.exists(filename)) self.editor.on_proc_closed(1, QProcess.NormalExit) self.assertFalse(os.path.exists(filename)) @@ -123,7 +125,7 @@ class FileHandlingTests(unittest.TestCase): def test_file_handling_closed_crash(self): """Test file handling when closing with a crash.""" self.editor.edit("") - filename = self.editor.filename + filename = self.editor._filename self.assertTrue(os.path.exists(filename)) self.editor.on_proc_error(QProcess.Crashed) self.editor.on_proc_closed(0, QProcess.CrashExit) @@ -150,7 +152,7 @@ class TextModifyTests(unittest.TestCase): Args: text: The text to write to the file. """ - filename = self.editor.filename + filename = self.editor._filename with open(filename, 'w', encoding='utf-8') as f: f.write(text) @@ -160,7 +162,7 @@ class TextModifyTests(unittest.TestCase): Return: The text which was read. """ - filename = self.editor.filename + filename = self.editor._filename with open(filename, 'r', encoding='utf-8') as f: data = f.read() return data diff --git a/qutebrowser/test/utils/test_log.py b/qutebrowser/test/utils/test_log.py index a2736383b..50c9f887b 100644 --- a/qutebrowser/test/utils/test_log.py +++ b/qutebrowser/test/utils/test_log.py @@ -171,18 +171,18 @@ class RAMHandlerTests(BaseTest): """Test handler with exactly as much records as it can hold.""" self.logger.debug("One") self.logger.debug("Two") - self.assertEqual(len(self.handler.data), 2) - self.assertEqual(self.handler.data[0].msg, "One") - self.assertEqual(self.handler.data[1].msg, "Two") + self.assertEqual(len(self.handler._data), 2) + self.assertEqual(self.handler._data[0].msg, "One") + self.assertEqual(self.handler._data[1].msg, "Two") def test_overflow(self): """Test handler with more records as it can hold.""" self.logger.debug("One") self.logger.debug("Two") self.logger.debug("Three") - self.assertEqual(len(self.handler.data), 2) - self.assertEqual(self.handler.data[0].msg, "Two") - self.assertEqual(self.handler.data[1].msg, "Three") + self.assertEqual(len(self.handler._data), 2) + self.assertEqual(self.handler._data[0].msg, "Two") + self.assertEqual(self.handler._data[1].msg, "Three") def test_dump_log(self): """Test dump_log().""" diff --git a/qutebrowser/test/utils/test_readline.py b/qutebrowser/test/utils/test_readline.py index c45b29bb3..0cdeda400 100644 --- a/qutebrowser/test/utils/test_readline.py +++ b/qutebrowser/test/utils/test_readline.py @@ -19,6 +19,8 @@ """Tests for qutebrowser.utils.readline.""" +# pylint: disable=protected-access + import inspect import unittest from unittest import mock @@ -105,7 +107,7 @@ class ReadlineBridgeTest(unittest.TestCase): self._set_selected_text("delete test") self.bridge.rl_unix_line_discard() self.qle.home.assert_called_with(True) - self.assertEqual(self.bridge.deleted[self.qle], "delete test") + self.assertEqual(self.bridge._deleted[self.qle], "delete test") self.qle.del_.assert_called_with() self.bridge.rl_yank() self.qle.insert.assert_called_with("delete test") @@ -115,7 +117,7 @@ class ReadlineBridgeTest(unittest.TestCase): self._set_selected_text("delete test") self.bridge.rl_kill_line() self.qle.end.assert_called_with(True) - self.assertEqual(self.bridge.deleted[self.qle], "delete test") + self.assertEqual(self.bridge._deleted[self.qle], "delete test") self.qle.del_.assert_called_with() self.bridge.rl_yank() self.qle.insert.assert_called_with("delete test") @@ -125,7 +127,7 @@ class ReadlineBridgeTest(unittest.TestCase): self._set_selected_text("delete test") self.bridge.rl_unix_word_rubout() self.qle.cursorWordBackward.assert_called_with(True) - self.assertEqual(self.bridge.deleted[self.qle], "delete test") + self.assertEqual(self.bridge._deleted[self.qle], "delete test") self.qle.del_.assert_called_with() self.bridge.rl_yank() self.qle.insert.assert_called_with("delete test") @@ -135,7 +137,7 @@ class ReadlineBridgeTest(unittest.TestCase): self._set_selected_text("delete test") self.bridge.rl_kill_word() self.qle.cursorWordForward.assert_called_with(True) - self.assertEqual(self.bridge.deleted[self.qle], "delete test") + self.assertEqual(self.bridge._deleted[self.qle], "delete test") self.qle.del_.assert_called_with() self.bridge.rl_yank() self.qle.insert.assert_called_with("delete test") diff --git a/qutebrowser/test/utils/usertypes/test_neighborlist.py b/qutebrowser/test/utils/usertypes/test_neighborlist.py index 4345899ec..3dd59b5ed 100644 --- a/qutebrowser/test/utils/usertypes/test_neighborlist.py +++ b/qutebrowser/test/utils/usertypes/test_neighborlist.py @@ -72,17 +72,17 @@ class DefaultTests(unittest.TestCase): def test_simple(self): """Test default with a numeric argument.""" nl = usertypes.NeighborList([1, 2, 3], default=2) - self.assertEqual(nl.idx, 1) + self.assertEqual(nl._idx, 1) def test_none(self): """Test default 'None'.""" nl = usertypes.NeighborList([1, 2, None], default=None) - self.assertEqual(nl.idx, 2) + self.assertEqual(nl._idx, 2) def test_unset(self): """Test unset default value.""" nl = usertypes.NeighborList([1, 2, 3]) - self.assertIsNone(nl.idx) + self.assertIsNone(nl._idx) class EmptyTests(unittest.TestCase): @@ -130,48 +130,48 @@ class ItemTests(unittest.TestCase): def test_curitem(self): """Test curitem().""" - self.assertEqual(self.nl.idx, 2) + self.assertEqual(self.nl._idx, 2) self.assertEqual(self.nl.curitem(), 3) - self.assertEqual(self.nl.idx, 2) + self.assertEqual(self.nl._idx, 2) def test_nextitem(self): """Test nextitem().""" self.assertEqual(self.nl.nextitem(), 4) - self.assertEqual(self.nl.idx, 3) + self.assertEqual(self.nl._idx, 3) self.assertEqual(self.nl.nextitem(), 5) - self.assertEqual(self.nl.idx, 4) + self.assertEqual(self.nl._idx, 4) def test_previtem(self): """Test previtem().""" self.assertEqual(self.nl.previtem(), 2) - self.assertEqual(self.nl.idx, 1) + self.assertEqual(self.nl._idx, 1) self.assertEqual(self.nl.previtem(), 1) - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) def test_firstitem(self): """Test firstitem().""" self.assertEqual(self.nl.firstitem(), 1) - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) def test_lastitem(self): """Test lastitem().""" self.assertEqual(self.nl.lastitem(), 5) - self.assertEqual(self.nl.idx, 4) + self.assertEqual(self.nl._idx, 4) def test_reset(self): """Test reset().""" self.nl.nextitem() - self.assertEqual(self.nl.idx, 3) + self.assertEqual(self.nl._idx, 3) self.nl.reset() - self.assertEqual(self.nl.idx, 2) + self.assertEqual(self.nl._idx, 2) def test_getitem(self): """Test getitem().""" self.assertEqual(self.nl.getitem(2), 5) - self.assertEqual(self.nl.idx, 4) + self.assertEqual(self.nl._idx, 4) self.nl.reset() self.assertEqual(self.nl.getitem(-2), 1) - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) class OneTests(unittest.TestCase): @@ -189,51 +189,51 @@ class OneTests(unittest.TestCase): """Test out of bounds previtem() with mode=wrap.""" self.nl._mode = usertypes.NeighborList.Modes.wrap self.nl.firstitem() - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) self.assertEqual(self.nl.previtem(), 1) - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) def test_first_block(self): """Test out of bounds previtem() with mode=block.""" self.nl._mode = usertypes.NeighborList.Modes.block self.nl.firstitem() - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) self.assertEqual(self.nl.previtem(), 1) - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) def test_first_raise(self): """Test out of bounds previtem() with mode=raise.""" self.nl._mode = usertypes.NeighborList.Modes.exception self.nl.firstitem() - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) with self.assertRaises(IndexError): self.nl.previtem() - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) def test_last_wrap(self): """Test out of bounds nextitem() with mode=wrap.""" self.nl._mode = usertypes.NeighborList.Modes.wrap self.nl.lastitem() - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) self.assertEqual(self.nl.nextitem(), 1) - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) def test_last_block(self): """Test out of bounds nextitem() with mode=block.""" self.nl._mode = usertypes.NeighborList.Modes.block self.nl.lastitem() - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) self.assertEqual(self.nl.nextitem(), 1) - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) def test_last_raise(self): """Test out of bounds nextitem() with mode=raise.""" self.nl._mode = usertypes.NeighborList.Modes.exception self.nl.lastitem() - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) with self.assertRaises(IndexError): self.nl.nextitem() - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) class BlockTests(unittest.TestCase): @@ -252,16 +252,16 @@ class BlockTests(unittest.TestCase): def test_first(self): """Test ouf of bounds previtem().""" self.nl.firstitem() - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) self.assertEqual(self.nl.previtem(), 1) - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) def test_last(self): """Test ouf of bounds nextitem().""" self.nl.lastitem() - self.assertEqual(self.nl.idx, 4) + self.assertEqual(self.nl._idx, 4) self.assertEqual(self.nl.nextitem(), 5) - self.assertEqual(self.nl.idx, 4) + self.assertEqual(self.nl._idx, 4) class WrapTests(unittest.TestCase): @@ -279,16 +279,16 @@ class WrapTests(unittest.TestCase): def test_first(self): """Test ouf of bounds previtem().""" self.nl.firstitem() - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) self.assertEqual(self.nl.previtem(), 5) - self.assertEqual(self.nl.idx, 4) + self.assertEqual(self.nl._idx, 4) def test_last(self): """Test ouf of bounds nextitem().""" self.nl.lastitem() - self.assertEqual(self.nl.idx, 4) + self.assertEqual(self.nl._idx, 4) self.assertEqual(self.nl.nextitem(), 1) - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) class RaiseTests(unittest.TestCase): @@ -307,18 +307,18 @@ class RaiseTests(unittest.TestCase): def test_first(self): """Test ouf of bounds previtem().""" self.nl.firstitem() - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) with self.assertRaises(IndexError): self.nl.previtem() - self.assertEqual(self.nl.idx, 0) + self.assertEqual(self.nl._idx, 0) def test_last(self): """Test ouf of bounds nextitem().""" self.nl.lastitem() - self.assertEqual(self.nl.idx, 4) + self.assertEqual(self.nl._idx, 4) with self.assertRaises(IndexError): self.nl.nextitem() - self.assertEqual(self.nl.idx, 4) + self.assertEqual(self.nl._idx, 4) class SnapInTests(unittest.TestCase): @@ -336,29 +336,29 @@ class SnapInTests(unittest.TestCase): """Test fuzzyval with snapping to a bigger value.""" self.nl.fuzzyval = 7 self.assertEqual(self.nl.nextitem(), 9) - self.assertEqual(self.nl.idx, 1) + self.assertEqual(self.nl._idx, 1) self.assertEqual(self.nl.nextitem(), 1) - self.assertEqual(self.nl.idx, 2) + self.assertEqual(self.nl._idx, 2) def test_smaller(self): """Test fuzzyval with snapping to a smaller value.""" self.nl.fuzzyval = 7 self.assertEqual(self.nl.previtem(), 5) - self.assertEqual(self.nl.idx, 3) + self.assertEqual(self.nl._idx, 3) self.assertEqual(self.nl.previtem(), 1) - self.assertEqual(self.nl.idx, 2) + self.assertEqual(self.nl._idx, 2) def test_equal_bigger(self): """Test fuzzyval with matching value, snapping to a bigger value.""" self.nl.fuzzyval = 20 self.assertEqual(self.nl.nextitem(), 9) - self.assertEqual(self.nl.idx, 1) + self.assertEqual(self.nl._idx, 1) def test_equal_smaller(self): """Test fuzzyval with matching value, snapping to a smaller value.""" self.nl.fuzzyval = 5 self.assertEqual(self.nl.previtem(), 1) - self.assertEqual(self.nl.idx, 2) + self.assertEqual(self.nl._idx, 2) if __name__ == '__main__': diff --git a/qutebrowser/utils/completer.py b/qutebrowser/utils/completer.py index 2b8a1dc27..c881daccd 100644 --- a/qutebrowser/utils/completer.py +++ b/qutebrowser/utils/completer.py @@ -33,7 +33,7 @@ class Completer(QObject): """Completer which manages completions in a CompletionView. Attributes: - ignore_change: Whether to ignore the next completion update. + _ignore_change: Whether to ignore the next completion update. _models: dict of available completion models. Signals: @@ -48,8 +48,8 @@ class Completer(QObject): change_completed_part = pyqtSignal(str, bool) def __init__(self, parent=None): - super().__init__() - self.ignore_change = False + super().__init__(parent) + self._ignore_change = False self._models = { usertypes.Completion.option: {}, @@ -177,9 +177,9 @@ class Completer(QObject): # and go on to the next part. self.change_completed_part.emit(data, True) else: - self.ignore_change = True + self._ignore_change = True self.change_completed_part.emit(data, False) - self.ignore_change = False + self._ignore_change = False @pyqtSlot(str, list, int) def on_update_completion(self, prefix, parts, cursor_part): @@ -191,7 +191,7 @@ class Completer(QObject): text: The new text cursor_part: The part the cursor is currently over. """ - if self.ignore_change: + if self._ignore_change: log.completion.debug("Ignoring completion update") return diff --git a/qutebrowser/utils/docutils.py b/qutebrowser/utils/docutils.py index 946e47394..4cb40a03e 100644 --- a/qutebrowser/utils/docutils.py +++ b/qutebrowser/utils/docutils.py @@ -75,12 +75,13 @@ class DocstringParser: Args: func: The function to parse the docstring for. """ - self.state = self.State.short + self._state = self.State.short + self._cur_arg_name = None self.short_desc = [] self.long_desc = [] self.arg_descs = collections.OrderedDict() - self.cur_arg_name = None - self.handlers = { + doc = inspect.getdoc(func) + handlers = { self.State.short: self._parse_short, self.State.desc: self._parse_desc, self.State.desc_hidden: self._skip, @@ -88,9 +89,8 @@ class DocstringParser: self.State.arg_inside: self._parse_arg_inside, self.State.misc: self._skip, } - doc = inspect.getdoc(func) for line in doc.splitlines(): - handler = self.handlers[self.state] + handler = handlers[self._state] stop = handler(line) if stop: break @@ -101,41 +101,41 @@ class DocstringParser: def _process_arg(self, line): """Helper method to process a line like 'fooarg: Blah blub'.""" - self.cur_arg_name, argdesc = line.split(':', maxsplit=1) - self.cur_arg_name = self.cur_arg_name.strip().lstrip('*') - self.arg_descs[self.cur_arg_name] = [argdesc.strip()] + self._cur_arg_name, argdesc = line.split(':', maxsplit=1) + self._cur_arg_name = self._cur_arg_name.strip().lstrip('*') + self.arg_descs[self._cur_arg_name] = [argdesc.strip()] def _skip(self, line): """Handler to ignore everything until we get 'Args:'.""" if line.startswith('Args:'): - self.state = self.State.arg_start + self._state = self.State.arg_start def _parse_short(self, line): """Parse the short description (first block) in the docstring.""" if not line: - self.state = self.State.desc + self._state = self.State.desc else: self.short_desc.append(line.strip()) def _parse_desc(self, line): """Parse the long description in the docstring.""" if line.startswith('Args:'): - self.state = self.State.arg_start + self._state = self.State.arg_start elif line.startswith('Emit:') or line.startswith('Raise:'): - self.state = self.State.misc + self._state = self.State.misc elif line.strip() == '//': - self.state = self.State.desc_hidden + self._state = self.State.desc_hidden elif line.strip(): self.long_desc.append(line.strip()) def _parse_arg_start(self, line): """Parse first argument line.""" self._process_arg(line) - self.state = self.State.arg_inside + self._state = self.State.arg_inside def _parse_arg_inside(self, line): """Parse subsequent argument lines.""" - argname = self.cur_arg_name + argname = self._cur_arg_name if re.match(r'^[A-Z][a-z]+:$', line): if not self.arg_descs[argname][-1].strip(): self.arg_descs[argname] = self.arg_descs[argname][:-1] diff --git a/qutebrowser/utils/editor.py b/qutebrowser/utils/editor.py index 23feeea89..c661901a3 100644 --- a/qutebrowser/utils/editor.py +++ b/qutebrowser/utils/editor.py @@ -36,16 +36,16 @@ class ExternalEditor(QObject): def __init__(self, parent=None): super().__init__(parent) - self.text = None - self.oshandle = None - self.filename = None - self.proc = None + self._text = None + self._oshandle = None + self._filename = None + self._proc = None def _cleanup(self): """Clean up temporary files after the editor closed.""" - os.close(self.oshandle) + os.close(self._oshandle) try: - os.remove(self.filename) + os.remove(self._filename) except PermissionError as e: # NOTE: Do not replace this with "raise CommandError" as it's # executed async. @@ -72,7 +72,7 @@ class ExternalEditor(QObject): exitcode)) return encoding = config.get('general', 'editor-encoding') - with open(self.filename, 'r', encoding=encoding) as f: + with open(self._filename, 'r', encoding=encoding) as f: text = ''.join(f.readlines()) log.procs.debug("Read back: {}".format(text)) self.editing_finished.emit(text) @@ -105,19 +105,19 @@ class ExternalEditor(QObject): Emit: editing_finished with the new text if editing finshed successfully. """ - if self.text is not None: + if self._text is not None: raise ValueError("Already editing a file!") - self.text = text - self.oshandle, self.filename = tempfile.mkstemp(text=True) + self._text = text + self._oshandle, self._filename = tempfile.mkstemp(text=True) if text: encoding = config.get('general', 'editor-encoding') - with open(self.filename, 'w', encoding=encoding) as f: + with open(self._filename, 'w', encoding=encoding) as f: f.write(text) - self.proc = QProcess(self) - self.proc.finished.connect(self.on_proc_closed) - self.proc.error.connect(self.on_proc_error) + self._proc = QProcess(self) + self._proc.finished.connect(self.on_proc_closed) + self._proc.error.connect(self.on_proc_error) editor = config.get('general', 'editor') executable = editor[0] - args = [self.filename if arg == '{}' else arg for arg in editor[1:]] + args = [self._filename if arg == '{}' else arg for arg in editor[1:]] log.procs.debug("Calling \"{}\" with args {}".format(executable, args)) - self.proc.start(executable, args) + self._proc.start(executable, args) diff --git a/qutebrowser/utils/jinja.py b/qutebrowser/utils/jinja.py index e04e05058..deac0eb7f 100644 --- a/qutebrowser/utils/jinja.py +++ b/qutebrowser/utils/jinja.py @@ -31,10 +31,10 @@ class Loader(jinja2.BaseLoader): """Jinja loader which uses utils.read_file to load templates.""" def __init__(self, subdir): - self.subdir = subdir + self._subdir = subdir def get_source(self, _env, template): - path = os.path.join(self.subdir, template) + path = os.path.join(self._subdir, template) try: source = utils.read_file(path) except FileNotFoundError: diff --git a/qutebrowser/utils/log.py b/qutebrowser/utils/log.py index 0e225609a..49e77a57d 100644 --- a/qutebrowser/utils/log.py +++ b/qutebrowser/utils/log.py @@ -295,21 +295,21 @@ class LogFilter(logging.Filter): comma-separated list instead. Attributes: - names: A list of names that should be logged. + _names: A list of names that should be logged. """ def __init__(self, names): super().__init__() - self.names = names + self._names = names def filter(self, record): """Determine if the specified record is to be logged.""" - if self.names is None: + if self._names is None: return True if record.levelno > logging.DEBUG: # More important than DEBUG, so we won't filter at all return True - for name in self.names: + for name in self._names: if record.name == name: return True elif not record.name.startswith(name): @@ -327,21 +327,21 @@ class RAMHandler(logging.Handler): uses a simple list rather than a deque. Attributes: - data: A deque containing the logging records. + _data: A deque containing the logging records. """ def __init__(self, capacity): super().__init__() self.html_formatter = None if capacity != -1: - self.data = collections.deque(maxlen=capacity) + self._data = collections.deque(maxlen=capacity) else: - self.data = collections.deque() + self._data = collections.deque() def emit(self, record): if record.levelno >= logging.DEBUG: # We don't log VDEBUG to RAM. - self.data.append(record) + self._data.append(record) def dump_log(self, html=False): """Dump the complete formatted log data as as string. @@ -352,7 +352,7 @@ class RAMHandler(logging.Handler): fmt = self.html_formatter.format if html else self.format self.acquire() try: - records = list(self.data) + records = list(self._data) finally: self.release() for record in records: diff --git a/qutebrowser/utils/readline.py b/qutebrowser/utils/readline.py index d398e2f7c..e38a10457 100644 --- a/qutebrowser/utils/readline.py +++ b/qutebrowser/utils/readline.py @@ -30,11 +30,11 @@ class ReadlineBridge: """Bridge which provides readline-like commands for the current QLineEdit. Attributes: - deleted: Mapping from widgets to their last deleted text. + _deleted: Mapping from widgets to their last deleted text. """ def __init__(self): - self.deleted = {} + self._deleted = {} def __repr__(self): return '<{}>'.format(self.__class__.__name__) @@ -130,7 +130,7 @@ class ReadlineBridge: if widget is None: return widget.home(True) - self.deleted[widget] = widget.selectedText() + self._deleted[widget] = widget.selectedText() widget.del_() @cmdutils.register(instance='readline-bridge', hide=True, @@ -144,7 +144,7 @@ class ReadlineBridge: if widget is None: return widget.end(True) - self.deleted[widget] = widget.selectedText() + self._deleted[widget] = widget.selectedText() widget.del_() @cmdutils.register(instance='readline-bridge', hide=True, @@ -158,7 +158,7 @@ class ReadlineBridge: if widget is None: return widget.cursorWordBackward(True) - self.deleted[widget] = widget.selectedText() + self._deleted[widget] = widget.selectedText() widget.del_() @cmdutils.register(instance='readline-bridge', hide=True, @@ -172,7 +172,7 @@ class ReadlineBridge: if widget is None: return widget.cursorWordForward(True) - self.deleted[widget] = widget.selectedText() + self._deleted[widget] = widget.selectedText() widget.del_() @cmdutils.register(instance='readline-bridge', hide=True, @@ -183,9 +183,9 @@ class ReadlineBridge: This acts like readline's yank. """ widget = self._widget() - if widget is None or widget not in self.deleted: + if widget is None or widget not in self._deleted: return - widget.insert(self.deleted[widget]) + widget.insert(self._deleted[widget]) @cmdutils.register(instance='readline-bridge', hide=True, modes=[typ.KeyMode.command, typ.KeyMode.prompt]) diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index e17e231ac..ef10406fd 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -71,8 +71,8 @@ class NeighborList(collections.abc.Sequence): Modes: Different modes, see constructor documentation. Attributes: - idx: The current position in the list. fuzzyval: The value which is currently set but not in the list. + _idx: The current position in the list. _items: A list of all items, accessed through item property. _mode: The current mode. """ @@ -98,9 +98,9 @@ class NeighborList(collections.abc.Sequence): self._items = list(items) self._default = default if default is not _UNSET: - self.idx = self._items.index(default) + self._idx = self._items.index(default) else: - self.idx = None + self._idx = None self._mode = mode self.fuzzyval = None @@ -128,7 +128,7 @@ class NeighborList(collections.abc.Sequence): items = [(idx, e) for (idx, e) in enumerate(self._items) if op(e, self.fuzzyval)] close_item = min(items, key=lambda tpl: abs(self.fuzzyval - tpl[1])) - self.idx = close_item[0] + self._idx = close_item[0] return self.fuzzyval not in self._items def _get_new_item(self, offset): @@ -145,21 +145,21 @@ class NeighborList(collections.abc.Sequence): exception. """ try: - if self.idx + offset >= 0: - new = self._items[self.idx + offset] + if self._idx + offset >= 0: + new = self._items[self._idx + offset] else: raise IndexError except IndexError: if self._mode == self.Modes.block: new = self.curitem() elif self._mode == self.Modes.wrap: - self.idx += offset - self.idx %= len(self.items) + self._idx += offset + self._idx %= len(self.items) new = self.curitem() elif self._mode == self.Modes.exception: raise else: - self.idx += offset + self._idx += offset return new @property @@ -181,7 +181,7 @@ class NeighborList(collections.abc.Sequence): exception. """ log.misc.debug("{} items, idx {}, offset {}".format( - len(self._items), self.idx, offset)) + len(self._items), self._idx, offset)) if not self._items: raise IndexError("No items found!") if self.fuzzyval is not None: @@ -198,8 +198,8 @@ class NeighborList(collections.abc.Sequence): def curitem(self): """Get the current item in the list.""" - if self.idx is not None: - return self._items[self.idx] + if self._idx is not None: + return self._items[self._idx] else: raise IndexError("No current item!") @@ -215,14 +215,14 @@ class NeighborList(collections.abc.Sequence): """Get the first item in the list.""" if not self._items: raise IndexError("No items found!") - self.idx = 0 + self._idx = 0 return self.curitem() def lastitem(self): """Get the last item in the list.""" if not self._items: raise IndexError("No items found!") - self.idx = len(self._items) - 1 + self._idx = len(self._items) - 1 return self.curitem() def reset(self): @@ -230,7 +230,7 @@ class NeighborList(collections.abc.Sequence): if self._default is _UNSET: raise ValueError("No default set!") else: - self.idx = self._items.index(self._default) + self._idx = self._items.index(self._default) return self.curitem() diff --git a/qutebrowser/utils/utils.py b/qutebrowser/utils/utils.py index 387c53d90..6502cd26c 100644 --- a/qutebrowser/utils/utils.py +++ b/qutebrowser/utils/utils.py @@ -532,8 +532,8 @@ class prevent_exceptions: # pylint: disable=invalid-name much cleaner to implement. Attributes: - retval: The value to return in case of an exception. - predicate: The condition which needs to be True to prevent exceptions + _retval: The value to return in case of an exception. + _predicate: The condition which needs to be True to prevent exceptions """ def __init__(self, retval, predicate=True): @@ -544,8 +544,8 @@ class prevent_exceptions: # pylint: disable=invalid-name Args: See class attributes. """ - self.retval = retval - self.predicate = predicate + self._retval = retval + self._predicate = predicate def __call__(self, func): """Gets called when a function should be decorated. @@ -556,10 +556,10 @@ class prevent_exceptions: # pylint: disable=invalid-name Return: The decorated function. """ - if not self.predicate: + if not self._predicate: return func - retval = self.retval + retval = self._retval @functools.wraps(func) def wrapper(*args, **kwargs): # pylint: disable=missing-docstring diff --git a/qutebrowser/widgets/console.py b/qutebrowser/widgets/console.py index fdd4c395b..b852019e6 100644 --- a/qutebrowser/widgets/console.py +++ b/qutebrowser/widgets/console.py @@ -56,7 +56,7 @@ class ConsoleLineEdit(misc.CommandLineEdit): 'self': parent, } self._interpreter = code.InteractiveInterpreter(interpreter_locals) - self.history = cmdhistory.History() + self._history = cmdhistory.History() self.returnPressed.connect(self.execute) self.setText('') @@ -67,10 +67,10 @@ class ConsoleLineEdit(misc.CommandLineEdit): @pyqtSlot(str) def execute(self): """Execute the line of code which was entered.""" - self.history.stop() + self._history.stop() text = self.text() if text: - self.history.append(text) + self._history.append(text) self.push(text) self.setText('') @@ -95,10 +95,10 @@ class ConsoleLineEdit(misc.CommandLineEdit): def history_prev(self): """Go back in the history.""" try: - if not self.history.is_browsing(): - item = self.history.start(self.text().strip()) + if not self._history.is_browsing(): + item = self._history.start(self.text().strip()) else: - item = self.history.previtem() + item = self._history.previtem() except (cmdhistory.HistoryEmptyError, cmdhistory.HistoryEndReachedError): return @@ -106,10 +106,10 @@ class ConsoleLineEdit(misc.CommandLineEdit): def history_next(self): """Go forward in the history.""" - if not self.history.is_browsing(): + if not self._history.is_browsing(): return try: - item = self.history.nextitem() + item = self._history.nextitem() except cmdhistory.HistoryEndReachedError: return self.setText(item) @@ -166,15 +166,15 @@ class ConsoleWidget(QWidget): def __init__(self, parent=None): super().__init__(parent) - self.lineedit = ConsoleLineEdit(self) - self.output = ConsoleTextEdit() - self.lineedit.write.connect(self.output.append) - self.vbox = QVBoxLayout() - self.vbox.setSpacing(0) - self.vbox.addWidget(self.output) - self.vbox.addWidget(self.lineedit) - self.setLayout(self.vbox) - self.lineedit.setFocus() + self._lineedit = ConsoleLineEdit(self) + self._output = ConsoleTextEdit() + self._lineedit.write.connect(self._output.append) + self._vbox = QVBoxLayout() + self._vbox.setSpacing(0) + self._vbox.addWidget(self._output) + self._vbox.addWidget(self._lineedit) + self.setLayout(self._vbox) + self._lineedit.setFocus() def __repr__(self): return '<{}, visible={}>'.format( @@ -183,5 +183,5 @@ class ConsoleWidget(QWidget): @pyqtSlot(str, str) def on_config_changed(self, section, option): """Update font when config changed.""" - self.lineedit.on_config_changed(section, option) - self.output.on_config_changed(section, option) + self._lineedit.on_config_changed(section, option) + self._output.on_config_changed(section, option) diff --git a/qutebrowser/widgets/mainwindow.py b/qutebrowser/widgets/mainwindow.py index ddb15e002..f16db872c 100644 --- a/qutebrowser/widgets/mainwindow.py +++ b/qutebrowser/widgets/mainwindow.py @@ -41,7 +41,7 @@ class MainWindow(QWidget): Attributes: status: The StatusBar widget. - downloadview: The DownloadView widget. + _downloadview: The DownloadView widget. _tabbed_browser: The TabbedBrowser widget. _vbox: The main QVBoxLayout. """ @@ -77,9 +77,9 @@ class MainWindow(QWidget): self._vbox.setContentsMargins(0, 0, 0, 0) self._vbox.setSpacing(0) - self.downloadview = downloads.DownloadView() - self._vbox.addWidget(self.downloadview) - self.downloadview.show() + self._downloadview = downloads.DownloadView() + self._vbox.addWidget(self._downloadview) + self._downloadview.show() self._tabbed_browser = tabbedbrowser.TabbedBrowser() self._tabbed_browser.title_changed.connect(self.setWindowTitle) @@ -164,7 +164,7 @@ class MainWindow(QWidget): """ super().resizeEvent(e) self.resize_completion() - self.downloadview.updateGeometry() + self._downloadview.updateGeometry() self._tabbed_browser.tabBar().refresh() def closeEvent(self, e): diff --git a/qutebrowser/widgets/statusbar/command.py b/qutebrowser/widgets/statusbar/command.py index a15a165d0..6e448eea5 100644 --- a/qutebrowser/widgets/statusbar/command.py +++ b/qutebrowser/widgets/statusbar/command.py @@ -73,7 +73,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): def __init__(self, parent=None): misc.CommandLineEdit.__init__(self, parent) misc.MinimalLineEditMixin.__init__(self) - self.cursor_part = 0 + self._cursor_part = 0 self.history.history = objreg.get('command-history').data self._empty_item_idx = None self.textEdited.connect(self.on_text_edited) @@ -124,7 +124,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): for i, part in enumerate(self.split()): if cursor_pos <= len(part): # foo| bar - self.cursor_part = i + self._cursor_part = i if spaces: self._empty_item_idx = i else: @@ -132,14 +132,14 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): break cursor_pos -= (len(part) + 1) # FIXME are spaces always 1 char? log.completion.debug("cursor_part {}, spaces {}".format( - self.cursor_part, spaces)) + self._cursor_part, spaces)) return @pyqtSlot() def on_cursor_position_changed(self): """Update completion when the cursor position changed.""" self.update_completion.emit(self.prefix(), self.split(), - self.cursor_part) + self._cursor_part) @pyqtSlot(str) def set_cmd_text(self, text): @@ -156,7 +156,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): if old_text != text: # We want the completion to pop out here. self.update_completion.emit(self.prefix(), self.split(), - self.cursor_part) + self._cursor_part) self.setFocus() self.show_cmd.emit() @@ -196,16 +196,16 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): """ parts = self.split() log.completion.debug("changing part {} to '{}'".format( - self.cursor_part, newtext)) - parts[self.cursor_part] = newtext + self._cursor_part, newtext)) + parts[self._cursor_part] = newtext # We want to place the cursor directly after the part we just changed. - cursor_str = self.prefix() + ' '.join(parts[:self.cursor_part + 1]) + cursor_str = self.prefix() + ' '.join(parts[:self._cursor_part + 1]) if immediate: # If we should complete immediately, we want to move the cursor by # one more char, to get to the next field. cursor_str += ' ' text = self.prefix() + ' '.join(parts) - if immediate and self.cursor_part == len(parts) - 1: + if immediate and self._cursor_part == len(parts) - 1: # If we should complete immediately and we're completing the last # part in the commandline, we automatically add a space. text += ' ' diff --git a/qutebrowser/widgets/statusbar/prompter.py b/qutebrowser/widgets/statusbar/prompter.py index 62d2ebfb4..228c30f9f 100644 --- a/qutebrowser/widgets/statusbar/prompter.py +++ b/qutebrowser/widgets/statusbar/prompter.py @@ -57,7 +57,7 @@ class Prompter: up the *new* question. Attributes: - question: A Question object with the question to be asked to the user. + _question: A Question object with the question to be asked to the user. _loops: A list of local EventLoops to spin in when blocking. _queue: A deque of waiting questions. """ @@ -86,7 +86,7 @@ class Prompter: if not self._busy: return None prompt = objreg.get('prompt') - ctx = PromptContext(question=self.question, + ctx = PromptContext(question=self._question, text=prompt.txt.text(), input_text=prompt.lineedit.text(), echo_mode=prompt.lineedit.echoMode(), @@ -107,7 +107,7 @@ class Prompter: prompt.hide_prompt.emit() self._busy = False return False - self.question = ctx.question + self._question = ctx.question prompt.txt.setText(ctx.text) prompt.lineedit.setText(ctx.input_text) prompt.lineedit.setEchoMode(ctx.echo_mode) @@ -115,7 +115,7 @@ class Prompter: return True def _display_question(self): - """Display the question saved in self.question. + """Display the question saved in self._question. Return: The mode which should be entered. @@ -124,30 +124,30 @@ class Prompter: ValueError if the set PromptMode is invalid. """ prompt = objreg.get('prompt') - if self.question.mode == usertypes.PromptMode.yesno: - if self.question.default is None: + if self._question.mode == usertypes.PromptMode.yesno: + if self._question.default is None: suffix = "" - elif self.question.default: + elif self._question.default: suffix = " (yes)" else: suffix = " (no)" - prompt.txt.setText(self.question.text + suffix) + prompt.txt.setText(self._question.text + suffix) prompt.lineedit.hide() mode = usertypes.KeyMode.yesno - elif self.question.mode == usertypes.PromptMode.text: - prompt.txt.setText(self.question.text) - if self.question.default: - prompt.lineedit.setText(self.question.default) + elif self._question.mode == usertypes.PromptMode.text: + prompt.txt.setText(self._question.text) + if self._question.default: + prompt.lineedit.setText(self._question.default) prompt.lineedit.show() mode = usertypes.KeyMode.prompt - elif self.question.mode == usertypes.PromptMode.user_pwd: - prompt.txt.setText(self.question.text) - if self.question.default: - prompt.lineedit.setText(self.question.default) + elif self._question.mode == usertypes.PromptMode.user_pwd: + prompt.txt.setText(self._question.text) + if self._question.default: + prompt.lineedit.setText(self._question.default) prompt.lineedit.show() mode = usertypes.KeyMode.prompt - elif self.question.mode == usertypes.PromptMode.alert: - prompt.txt.setText(self.question.text + ' (ok)') + elif self._question.mode == usertypes.PromptMode.alert: + prompt.txt.setText(self._question.text + ' (ok)') prompt.lineedit.hide() mode = usertypes.KeyMode.prompt else: @@ -184,8 +184,8 @@ class Prompter: prompt.lineedit.setEchoMode(QLineEdit.Normal) prompt.hide_prompt.emit() self._busy = False - if self.question.answer is None and not self.question.is_aborted: - self.question.cancel() + if self._question.answer is None and not self._question.is_aborted: + self._question.cancel() @cmdutils.register(instance='prompter', hide=True, modes=[usertypes.KeyMode.prompt, @@ -199,34 +199,34 @@ class Prompter: for the password or leaves the mode. """ prompt = objreg.get('prompt') - if (self.question.mode == usertypes.PromptMode.user_pwd and - self.question.user is None): + if (self._question.mode == usertypes.PromptMode.user_pwd and + self._question.user is None): # User just entered an username - self.question.user = prompt.lineedit.text() + self._question.user = prompt.lineedit.text() prompt.txt.setText("Password:") prompt.lineedit.clear() prompt.lineedit.setEchoMode(QLineEdit.Password) - elif self.question.mode == usertypes.PromptMode.user_pwd: + elif self._question.mode == usertypes.PromptMode.user_pwd: # User just entered a password password = prompt.lineedit.text() - self.question.answer = (self.question.user, password) + self._question.answer = (self._question.user, password) modeman.leave(usertypes.KeyMode.prompt, 'prompt accept') - self.question.done() - elif self.question.mode == usertypes.PromptMode.text: + self._question.done() + elif self._question.mode == usertypes.PromptMode.text: # User just entered text. - self.question.answer = prompt.lineedit.text() + self._question.answer = prompt.lineedit.text() modeman.leave(usertypes.KeyMode.prompt, 'prompt accept') - self.question.done() - elif self.question.mode == usertypes.PromptMode.yesno: + self._question.done() + elif self._question.mode == usertypes.PromptMode.yesno: # User wants to accept the default of a yes/no question. - self.question.answer = self.question.default + self._question.answer = self._question.default modeman.leave(usertypes.KeyMode.yesno, 'yesno accept') - self.question.done() - elif self.question.mode == usertypes.PromptMode.alert: + self._question.done() + elif self._question.mode == usertypes.PromptMode.alert: # User acknowledged an alert - self.question.answer = None + self._question.answer = None modeman.leave(usertypes.KeyMode.prompt, 'alert accept') - self.question.done() + self._question.done() else: raise ValueError("Invalid question mode!") @@ -234,23 +234,23 @@ class Prompter: modes=[usertypes.KeyMode.yesno]) def prompt_yes(self): """Answer yes to a yes/no prompt.""" - if self.question.mode != usertypes.PromptMode.yesno: + if self._question.mode != usertypes.PromptMode.yesno: # We just ignore this if we don't have a yes/no question. return - self.question.answer = True + self._question.answer = True modeman.leave(usertypes.KeyMode.yesno, 'yesno accept') - self.question.done() + self._question.done() @cmdutils.register(instance='prompter', hide=True, modes=[usertypes.KeyMode.yesno]) def prompt_no(self): """Answer no to a yes/no prompt.""" - if self.question.mode != usertypes.PromptMode.yesno: + if self._question.mode != usertypes.PromptMode.yesno: # We just ignore this if we don't have a yes/no question. return - self.question.answer = False + self._question.answer = False modeman.leave(usertypes.KeyMode.yesno, 'prompt accept') - self.question.done() + self._question.done() @pyqtSlot(usertypes.Question, bool) def ask_question(self, question, blocking): @@ -280,7 +280,7 @@ class Prompter: # restore it after exec, if exec gets called multiple times. context = self._get_ctx() - self.question = question + self._question = question mode = self._display_question() question.aborted.connect(lambda: modeman.maybe_leave(mode, 'aborted')) mode_manager = objreg.get('mode-manager') @@ -303,6 +303,6 @@ class Prompter: # questions. if self._queue: self._pop_later() - return self.question.answer + return self._question.answer else: question.completed.connect(self._pop_later) From cb35452cf06571e5a659ef7135d82b0858c3605a Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 22:18:22 +0200 Subject: [PATCH 63/79] Remove unused flag for keyparser. --- qutebrowser/keyinput/keyparser.py | 1 - 1 file changed, 1 deletion(-) diff --git a/qutebrowser/keyinput/keyparser.py b/qutebrowser/keyinput/keyparser.py index 20b8cf966..4c9254eb7 100644 --- a/qutebrowser/keyinput/keyparser.py +++ b/qutebrowser/keyinput/keyparser.py @@ -65,7 +65,6 @@ class PassthroughKeyParser(CommandKeyParser): warn: Whether to warn if an ignored key was bound. """ super().__init__(parent, supports_chains=False) - self.log = False self._warn_on_keychains = warn self.read_config(mode) self._mode = mode From f2b46dc2e93f171af34a35023489cd683c5bd3cf Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 22:18:53 +0200 Subject: [PATCH 64/79] Remove srcmodel attribute from completionfilter. --- qutebrowser/models/completionfilter.py | 21 ++++++++++----------- qutebrowser/utils/completer.py | 2 +- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/qutebrowser/models/completionfilter.py b/qutebrowser/models/completionfilter.py index e4e721b30..ecdb6a7c9 100644 --- a/qutebrowser/models/completionfilter.py +++ b/qutebrowser/models/completionfilter.py @@ -34,14 +34,12 @@ class CompletionFilterModel(QSortFilterProxyModel): """Subclass of QSortFilterProxyModel with custom sorting/filtering. Attributes: - srcmodel: The source model of this QSFPM. _pattern: The pattern to filter with. """ def __init__(self, source, parent=None): super().__init__(parent) super().setSourceModel(source) - self.srcmodel = source self._pattern = '' def set_pattern(self, val): @@ -59,7 +57,7 @@ class CompletionFilterModel(QSortFilterProxyModel): self.invalidateFilter() sortcol = 0 try: - self.srcmodel.sort(sortcol) + self.sourceModel().sort(sortcol) except NotImplementedError: self.sort(sortcol) self.invalidate() @@ -109,13 +107,12 @@ class CompletionFilterModel(QSortFilterProxyModel): qtutils.ensure_valid(index) index = self.mapToSource(index) qtutils.ensure_valid(index) - self.srcmodel.mark_item(index, text) + self.sourceModel().mark_item(index, text) def setSourceModel(self, model): """Override QSortFilterProxyModel's setSourceModel to clear pattern.""" log.completion.debug("Setting source model: {}".format(model)) self.set_pattern('') - self.srcmodel = model super().setSourceModel(model) def filterAcceptsRow(self, row, parent): @@ -133,9 +130,9 @@ class CompletionFilterModel(QSortFilterProxyModel): """ if parent == QModelIndex(): return True - idx = self.srcmodel.index(row, 0, parent) + idx = self.sourceModel().index(row, 0, parent) qtutils.ensure_valid(idx) - data = self.srcmodel.data(idx) + data = self.sourceModel().data(idx) # TODO more sophisticated filtering if not self._pattern: return True @@ -157,14 +154,16 @@ class CompletionFilterModel(QSortFilterProxyModel): qtutils.ensure_valid(lindex) qtutils.ensure_valid(rindex) - left_sort = self.srcmodel.data(lindex, role=completion.Role.sort) - right_sort = self.srcmodel.data(rindex, role=completion.Role.sort) + srcmodel = self.sourceModel() + + left_sort = srcmodel.data(lindex, role=completion.Role.sort) + right_sort = srcmodel.data(rindex, role=completion.Role.sort) if left_sort is not None and right_sort is not None: return left_sort < right_sort - left = self.srcmodel.data(lindex) - right = self.srcmodel.data(rindex) + left = srcmodel.data(lindex) + right = srcmodel.data(rindex) leftstart = left.startswith(self._pattern) rightstart = right.startswith(self._pattern) diff --git a/qutebrowser/utils/completer.py b/qutebrowser/utils/completer.py index c881daccd..b573804e4 100644 --- a/qutebrowser/utils/completer.py +++ b/qutebrowser/utils/completer.py @@ -221,7 +221,7 @@ class Completer(QObject): log.completion.debug( "New completion for {}: {}, with pattern '{}'".format( - parts, model.srcmodel.__class__.__name__, pattern)) + parts, model.sourceModel().__class__.__name__, pattern)) if self._model().count() == 0: completion.hide() From 05ff908c4f70d82c0a728b442617b479efb9fee4 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 22:22:02 +0200 Subject: [PATCH 65/79] Update attributes in docstrings. --- qutebrowser/commands/argparser.py | 12 ++++++++++-- qutebrowser/commands/userscripts.py | 3 +++ qutebrowser/utils/docutils.py | 7 +++++++ qutebrowser/utils/editor.py | 9 ++++++++- qutebrowser/utils/jinja.py | 6 +++++- qutebrowser/utils/log.py | 7 ++++++- qutebrowser/widgets/console.py | 17 +++++++++++++++-- qutebrowser/widgets/misc.py | 6 +++++- qutebrowser/widgets/statusbar/command.py | 4 +--- 9 files changed, 60 insertions(+), 11 deletions(-) diff --git a/qutebrowser/commands/argparser.py b/qutebrowser/commands/argparser.py index 9ef3d59aa..f2493825e 100644 --- a/qutebrowser/commands/argparser.py +++ b/qutebrowser/commands/argparser.py @@ -38,7 +38,11 @@ class ArgumentParserError(Exception): class ArgumentParserExit(Exception): - """Exception raised when the argument parser exitted.""" + """Exception raised when the argument parser exitted. + + Attributes: + status: The exit status. + """ def __init__(self, status, msg): self.status = status @@ -61,7 +65,11 @@ class HelpAction(argparse.Action): class ArgumentParser(argparse.ArgumentParser): - """Subclass ArgumentParser to be more suitable for runtime parsing.""" + """Subclass ArgumentParser to be more suitable for runtime parsing. + + Attributes: + name: The command name. + """ def __init__(self, name, *args, **kwargs): self.name = name diff --git a/qutebrowser/commands/userscripts.py b/qutebrowser/commands/userscripts.py index f412ec323..47f38846c 100644 --- a/qutebrowser/commands/userscripts.py +++ b/qutebrowser/commands/userscripts.py @@ -258,6 +258,9 @@ class _WindowsUserscriptRunner(_BaseUserscriptRunner): This also means the userscript *has* to use >> (append) rather than > (overwrite) to write to the file! + + Attributes: + _oshandle: The oshandle of the temp file. """ def __init__(self, parent=None): diff --git a/qutebrowser/utils/docutils.py b/qutebrowser/utils/docutils.py index 4cb40a03e..459646304 100644 --- a/qutebrowser/utils/docutils.py +++ b/qutebrowser/utils/docutils.py @@ -64,6 +64,13 @@ class DocstringParser: """Generate documentation based on a docstring of a command handler. The docstring needs to follow the format described in HACKING. + + Attributes: + _state: The current state of the parser state machine. + _cur_arg_name: The name of the argument we're currently handling. + short_desc: The short description of the function. + long_desc: The long description of the function. + arg_descs: A dict of argument names to their descriptions """ State = usertypes.enum('State', 'short', 'desc', 'desc_hidden', diff --git a/qutebrowser/utils/editor.py b/qutebrowser/utils/editor.py index c661901a3..1d8ea3d68 100644 --- a/qutebrowser/utils/editor.py +++ b/qutebrowser/utils/editor.py @@ -30,7 +30,14 @@ from qutebrowser.utils import message, log class ExternalEditor(QObject): - """Class to simplify editing a text in an external editor.""" + """Class to simplify editing a text in an external editor. + + Attributes: + _text: The current text before the editor is opened. + _oshandle: The OS level handle to the tmpfile. + _filehandle: The file handle to the tmpfile. + _proc: The QProcess of the editor. + """ editing_finished = pyqtSignal(str) diff --git a/qutebrowser/utils/jinja.py b/qutebrowser/utils/jinja.py index deac0eb7f..62148aa80 100644 --- a/qutebrowser/utils/jinja.py +++ b/qutebrowser/utils/jinja.py @@ -28,7 +28,11 @@ from qutebrowser.utils import utils class Loader(jinja2.BaseLoader): - """Jinja loader which uses utils.read_file to load templates.""" + """Jinja loader which uses utils.read_file to load templates. + + Attributes: + _subdir: The subdirectory to find templates in. + """ def __init__(self, subdir): self._subdir = subdir diff --git a/qutebrowser/utils/log.py b/qutebrowser/utils/log.py index 49e77a57d..dc48312e4 100644 --- a/qutebrowser/utils/log.py +++ b/qutebrowser/utils/log.py @@ -362,7 +362,12 @@ class RAMHandler(logging.Handler): class HTMLFormatter(logging.Formatter): - """Formatter for HTML-colored log messages, similiar to colorlog.""" + """Formatter for HTML-colored log messages, similiar to colorlog. + + Attributes: + _log_colors: The colors to use for logging levels. + _colordict: The colordict passed to the logger. + """ def __init__(self, fmt, datefmt, log_colors): """Constructor. diff --git a/qutebrowser/widgets/console.py b/qutebrowser/widgets/console.py index b852019e6..b99ccf937 100644 --- a/qutebrowser/widgets/console.py +++ b/qutebrowser/widgets/console.py @@ -33,7 +33,14 @@ from qutebrowser.widgets import misc class ConsoleLineEdit(misc.CommandLineEdit): - """A QLineEdit which executes entered code and provides a history.""" + """A QLineEdit which executes entered code and provides a history. + + Attributes: + _history: The command history of executed commands. + _more: A flag which is set when more input is expected. + _buffer: The buffer for multiline commands. + _interpreter: The InteractiveInterpreter to execute code with. + """ write = pyqtSignal(str) @@ -162,7 +169,13 @@ class ConsoleTextEdit(QTextEdit): class ConsoleWidget(QWidget): - """A widget with an interactive Python console.""" + """A widget with an interactive Python console. + + Attributes: + _lineedit: The line edit in the console. + _output: The output widget in the console. + _vbox: The layout which contains everything. + """ def __init__(self, parent=None): super().__init__(parent) diff --git a/qutebrowser/widgets/misc.py b/qutebrowser/widgets/misc.py index 2f6bfa903..de64c578d 100644 --- a/qutebrowser/widgets/misc.py +++ b/qutebrowser/widgets/misc.py @@ -98,7 +98,11 @@ class CommandLineEdit(QLineEdit): class _CommandValidator(QValidator): - """Validator to prevent the : from getting deleted.""" + """Validator to prevent the : from getting deleted. + + Attributes: + prompt: The current prompt. + """ def __init__(self, parent=None): super().__init__(parent) diff --git a/qutebrowser/widgets/statusbar/command.py b/qutebrowser/widgets/statusbar/command.py index 6e448eea5..f88d16b90 100644 --- a/qutebrowser/widgets/statusbar/command.py +++ b/qutebrowser/widgets/statusbar/command.py @@ -34,9 +34,7 @@ class Command(misc.MinimalLineEditMixin, misc.CommandLineEdit): """The commandline part of the statusbar. Attributes: - cursor_part: The part the cursor is currently over. - parts: A list of strings with the split commandline - prefix: The prefix currently entered. + _cursor_part: The part the cursor is currently over. Signals: got_cmd: Emitted when a command is triggered by the user. From 027e7e054e8f68581e29eb2c404712658d8c4583 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 22:23:22 +0200 Subject: [PATCH 66/79] Remove obsolete EnumBase usertype. --- qutebrowser/utils/usertypes.py | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index ef10406fd..ff0bbe572 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -51,18 +51,6 @@ def enum(name, *items, start=1, is_int=False): return base(name, enums) -class EnumBase(type): - - """Metaclass for enums to provide __getitem__ for reverse mapping.""" - - def __init__(cls, name, base, fields): - super().__init__(name, base, fields) - cls._mapping = dict((v, k) for k, v in fields.items()) - - def __getitem__(cls, key): - return cls._mapping[key] - - class NeighborList(collections.abc.Sequence): """A list of items which saves it current position. From 34a5ad48b258cd4b92e160b3b48a047f9a116fcc Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 22:23:36 +0200 Subject: [PATCH 67/79] Don't pass prompt object to prompter. --- qutebrowser/widgets/statusbar/prompt.py | 2 +- qutebrowser/widgets/statusbar/prompter.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/qutebrowser/widgets/statusbar/prompt.py b/qutebrowser/widgets/statusbar/prompt.py index 46df80498..fde0afe70 100644 --- a/qutebrowser/widgets/statusbar/prompt.py +++ b/qutebrowser/widgets/statusbar/prompt.py @@ -66,7 +66,7 @@ class Prompt(QWidget): self.lineedit = PromptLineEdit() self._hbox.addWidget(self.lineedit) - prompter_obj = prompter.Prompter(self) + prompter_obj = prompter.Prompter() objreg.register('prompter', prompter_obj) def __repr__(self): diff --git a/qutebrowser/widgets/statusbar/prompter.py b/qutebrowser/widgets/statusbar/prompter.py index 228c30f9f..fa3119de1 100644 --- a/qutebrowser/widgets/statusbar/prompter.py +++ b/qutebrowser/widgets/statusbar/prompter.py @@ -62,8 +62,8 @@ class Prompter: _queue: A deque of waiting questions. """ - def __init__(self, prompt): - self.question = None + def __init__(self): + self._question = None self._loops = [] self._queue = collections.deque() self._busy = False From c19b8fe982751bc7b807d84cd19d3ffa7b9c9f98 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Wed, 24 Sep 2014 23:11:17 +0200 Subject: [PATCH 68/79] Fix tests for object registry. --- .../test/keyinput/test_basekeyparser.py | 33 ++++++++++++------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/qutebrowser/test/keyinput/test_basekeyparser.py b/qutebrowser/test/keyinput/test_basekeyparser.py index 7f4c356c4..8f8dcf660 100644 --- a/qutebrowser/test/keyinput/test_basekeyparser.py +++ b/qutebrowser/test/keyinput/test_basekeyparser.py @@ -29,6 +29,7 @@ from PyQt5.QtCore import Qt from qutebrowser.keyinput import basekeyparser from qutebrowser.test import stubs, helpers +from qutebrowser.utils import objreg CONFIG = {'input': {'timeout': 100}} @@ -42,6 +43,10 @@ BINDINGS = {'test': {'': 'ctrla', 'test2': {'foo': 'bar', '': 'ctrlx'}} +fake_keyconfig = mock.Mock(spec=['get_bindings_for']) +fake_keyconfig.get_bindings_for.side_effect = lambda s: BINDINGS[s] + + def setUpModule(): """Mock out some imports in basekeyparser.""" basekeyparser.QObject = mock.Mock() @@ -53,14 +58,6 @@ def tearDownModule(): logging.disable(logging.NOTSET) -def _get_fake_application(): - """Construct a fake QApplication with a keyconfig.""" - app = stubs.FakeQApplication() - app.keyconfig = mock.Mock(spec=['get_bindings_for']) - app.keyconfig.get_bindings_for.side_effect = lambda s: BINDINGS[s] - return app - - class SplitCountTests(unittest.TestCase): """Test the _split_count method. @@ -109,9 +106,12 @@ class ReadConfigTests(unittest.TestCase): """Test reading the config.""" def setUp(self): - basekeyparser.QCoreApplication = _get_fake_application() + objreg.register('key-config', fake_keyconfig) basekeyparser.usertypes.Timer = mock.Mock() + def tearDown(self): + objreg.delete('key-config') + def test_read_config_invalid(self): """Test reading config without setting it before.""" kp = basekeyparser.BaseKeyParser() @@ -145,12 +145,15 @@ class SpecialKeysTests(unittest.TestCase): 'qutebrowser.keyinput.basekeyparser.usertypes.Timer', autospec=True) patcher.start() + objreg.register('key-config', fake_keyconfig) self.addCleanup(patcher.stop) - basekeyparser.QCoreApplication = _get_fake_application() self.kp = basekeyparser.BaseKeyParser() self.kp.execute = mock.Mock() self.kp.read_config('test') + def tearDown(self): + objreg.delete('key-config') + def test_valid_key(self): """Test a valid special keyevent.""" self.kp.handle(helpers.fake_keyevent(Qt.Key_A, Qt.ControlModifier)) @@ -181,7 +184,7 @@ class KeyChainTests(unittest.TestCase): def setUp(self): """Set up mocks and read the test config.""" - basekeyparser.QCoreApplication = _get_fake_application() + objreg.register('key-config', fake_keyconfig) self.timermock = mock.Mock() basekeyparser.usertypes.Timer = mock.Mock(return_value=self.timermock) self.kp = basekeyparser.BaseKeyParser(supports_chains=True, @@ -189,6 +192,9 @@ class KeyChainTests(unittest.TestCase): self.kp.execute = mock.Mock() self.kp.read_config('test') + def tearDown(self): + objreg.delete('key-config') + def test_valid_special_key(self): """Test valid special key.""" self.kp.handle(helpers.fake_keyevent(Qt.Key_A, Qt.ControlModifier)) @@ -246,7 +252,7 @@ class CountTests(unittest.TestCase): """Test execute() with counts.""" def setUp(self): - basekeyparser.QCoreApplication = _get_fake_application() + objreg.register('key-config', fake_keyconfig) basekeyparser.usertypes.Timer = mock.Mock() self.kp = basekeyparser.BaseKeyParser(supports_chains=True, supports_count=True) @@ -296,6 +302,9 @@ class CountTests(unittest.TestCase): self.kp.execute.assert_called_once_with('ccc', self.kp.Type.chain, 23) self.assertEqual(self.kp._keystring, '') + def tearDown(self): + objreg.delete('key-config') + if __name__ == '__main__': unittest.main() From 4783df8c32366d5c8f9beb78e39db3d39d798962 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 25 Sep 2014 00:29:29 +0200 Subject: [PATCH 69/79] Avoid download_get signal. --- qutebrowser/app.py | 1 - qutebrowser/browser/commands.py | 3 +-- qutebrowser/browser/hints.py | 9 +++------ qutebrowser/widgets/tabbedbrowser.py | 3 --- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 1417d6791..cbaf77282 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -459,7 +459,6 @@ class Application(QApplication): # downloads tabs.start_download.connect(download_manager.fetch) - tabs.download_get.connect(download_manager.get) def _get_widgets(self): """Get a string list of all widgets.""" diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 25e6e0d8f..27e1219a0 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -766,8 +766,7 @@ class CommandDispatcher: def download_page(self): """Download the current page.""" page = self._current_widget().page() - tabbed_browser = objreg.get('tabbed-browser') - tabbed_browser.download_get.emit(self._current_url(), page) + objreg.get('download-manager').get(self._current_url(), page) @cmdutils.register(instance='command-dispatcher') def view_source(self): diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 90ffddf2f..875d1eb4a 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -31,7 +31,8 @@ from qutebrowser.config import config from qutebrowser.keyinput import modeman from qutebrowser.browser import webelem from qutebrowser.commands import userscripts, cmdexc -from qutebrowser.utils import usertypes, log, qtutils, message, objreg +from qutebrowser.utils import (usertypes, log, qtutils, message, objreg, + cmdutils) ElemTuple = collections.namedtuple('ElemTuple', 'elem, label') @@ -103,9 +104,6 @@ class HintManager(QObject): arg 0: URL to open as QUrl. arg 1: True if it should be opened in a new tab, else False. set_open_target: Set a new target to open the links in. - download_get: Download an URL. - arg 0: The URL to download, as QUrl. - arg 1: The QWebPage to download the URL in. """ HINT_CSS = """ @@ -139,7 +137,6 @@ class HintManager(QObject): mouse_event = pyqtSignal('QMouseEvent') openurl = pyqtSignal('QUrl', bool) set_open_target = pyqtSignal(str) - download_get = pyqtSignal('QUrl', 'QWebPage') def __init__(self, parent=None): """Constructor. @@ -362,7 +359,7 @@ class HintManager(QObject): immediately=True) return qtutils.ensure_valid(url) - self.download_get.emit(url, elem.webFrame().page()) + cmdreg.get('download-manager').get(url, elem.webFrame().page()) def _call_userscript(self, url): """Call an userscript from a hint.""" diff --git a/qutebrowser/widgets/tabbedbrowser.py b/qutebrowser/widgets/tabbedbrowser.py index ef91eb665..f07443197 100644 --- a/qutebrowser/widgets/tabbedbrowser.py +++ b/qutebrowser/widgets/tabbedbrowser.py @@ -78,7 +78,6 @@ class TabbedBrowser(tabwidget.TabWidget): current_tab_changed: The current tab changed to the emitted WebView. title_changed: Emitted when the application title should be changed. arg: The new title as string. - download_get: Emitted when a QUrl should be downloaded. """ cur_progress = pyqtSignal(int) @@ -90,7 +89,6 @@ class TabbedBrowser(tabwidget.TabWidget): cur_scroll_perc_changed = pyqtSignal(int, int) cur_load_status_changed = pyqtSignal(str) start_download = pyqtSignal('QNetworkReply*') - download_get = pyqtSignal('QUrl', 'QWebPage') hint_strings_updated = pyqtSignal(list) quit = pyqtSignal() resized = pyqtSignal('QRect') @@ -155,7 +153,6 @@ class TabbedBrowser(tabwidget.TabWidget): functools.partial(self.on_url_text_changed, tab)) # hintmanager tab.hintmanager.hint_strings_updated.connect(self.hint_strings_updated) - tab.hintmanager.download_get.connect(self.download_get) tab.hintmanager.openurl.connect(self.openurl) self.cur_load_started.connect(self.on_cur_load_started) # downloads From c77057e88e2a897bb8482a86b3cde9997772b85a Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 25 Sep 2014 00:30:52 +0200 Subject: [PATCH 70/79] Add a scope parameter to objreg functions. --- qutebrowser/utils/objreg.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/qutebrowser/utils/objreg.py b/qutebrowser/utils/objreg.py index 45bc5fed3..4a425ff96 100644 --- a/qutebrowser/utils/objreg.py +++ b/qutebrowser/utils/objreg.py @@ -77,14 +77,24 @@ meta_registry = ObjectRegistry() meta_registry['global'] = global_registry -def get(name, default=_UNSET): +def _get_registry(scope): + """Get the correct registry for a given scope.""" + if scope == 'global': + return global_registry + else: + raise ValueError("Invalid scope '{}'!".format(scope)) + + + +def get(name, default=_UNSET, scope='global'): """Helper function to get an object. Args: default: A default to return if the object does not exist. """ + reg = _get_registry(scope) try: - return global_registry[name] + return reg[name] except KeyError: if default is not _UNSET: return default @@ -92,7 +102,7 @@ def get(name, default=_UNSET): raise -def register(name, obj, update=False): +def register(name, obj, update=False, scope='global'): """Helper function to register an object. Args: @@ -100,12 +110,14 @@ def register(name, obj, update=False): obj: The object to register. update: If True, allows to update an already registered object. """ - if not update and name in global_registry: + reg = _get_registry(scope) + if not update and name in reg: raise KeyError("Object '{}' is already registered ({})!".format( - name, repr(global_registry[name]))) - global_registry[name] = obj + name, repr(reg[name]))) + reg[name] = obj -def delete(name): +def delete(name, scope='global'): """Helper function to unregister an object.""" - del global_registry[name] + reg = _get_registry(scope) + del reg[name] From df5ac3ab2fcc637f22b6019821b2dfacccc2c0e9 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 25 Sep 2014 07:41:18 +0200 Subject: [PATCH 71/79] Use object registry for keyparsers. --- qutebrowser/app.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index cbaf77282..19d5eec0e 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -55,7 +55,6 @@ class Application(QApplication): Attributes: _args: ArgumentParser instance. _commandrunner: The main CommandRunner instance. - _keyparsers: A mapping from modes to keyparsers. _shutting_down: True if we're currently shutting down. _quit_status: The current quitting status. _crashdlg: The crash dialog currently open. @@ -83,7 +82,6 @@ class Application(QApplication): } objreg.register('app', self) self._shutting_down = False - self._keyparsers = None self._crashdlg = None self._crashlogfile = None @@ -209,7 +207,7 @@ class Application(QApplication): def _init_modes(self): """Inizialize the mode manager and the keyparsers.""" - self._keyparsers = { + keyparsers = { utypes.KeyMode.normal: modeparsers.NormalKeyParser(self), utypes.KeyMode.hint: @@ -225,27 +223,28 @@ class Application(QApplication): utypes.KeyMode.yesno: modeparsers.PromptKeyParser(self), } + objreg.register('keyparsers', keyparsers) mode_manager = modeman.ModeManager(self) objreg.register('mode-manager', mode_manager) mode_manager.register(utypes.KeyMode.normal, - self._keyparsers[utypes.KeyMode.normal].handle) + keyparsers[utypes.KeyMode.normal].handle) mode_manager.register(utypes.KeyMode.hint, - self._keyparsers[utypes.KeyMode.hint].handle) + keyparsers[utypes.KeyMode.hint].handle) mode_manager.register(utypes.KeyMode.insert, - self._keyparsers[utypes.KeyMode.insert].handle, + keyparsers[utypes.KeyMode.insert].handle, passthrough=True) mode_manager.register( utypes.KeyMode.passthrough, - self._keyparsers[utypes.KeyMode.passthrough].handle, + keyparsers[utypes.KeyMode.passthrough].handle, passthrough=True) mode_manager.register(utypes.KeyMode.command, - self._keyparsers[utypes.KeyMode.command].handle, + keyparsers[utypes.KeyMode.command].handle, passthrough=True) mode_manager.register(utypes.KeyMode.prompt, - self._keyparsers[utypes.KeyMode.prompt].handle, + keyparsers[utypes.KeyMode.prompt].handle, passthrough=True) mode_manager.register(utypes.KeyMode.yesno, - self._keyparsers[utypes.KeyMode.yesno].handle) + keyparsers[utypes.KeyMode.yesno].handle) def _init_misc(self): """Initialize misc things.""" @@ -369,7 +368,7 @@ class Application(QApplication): """Connect all signals to their slots.""" # pylint: disable=too-many-statements, too-many-locals # syntactic sugar - kp = self._keyparsers + kp = objreg.get('keyparsers') main_window = objreg.get('main-window') status = main_window.status completion = objreg.get('completion') From e527db156013dbf740c05c8d5566c7cb34884a2f Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 25 Sep 2014 07:41:51 +0200 Subject: [PATCH 72/79] Add scope argument to cmdutils.register/commands. --- qutebrowser/commands/cmdutils.py | 14 ++++++++------ qutebrowser/commands/command.py | 6 ++++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/qutebrowser/commands/cmdutils.py b/qutebrowser/commands/cmdutils.py index 6f499f85d..68467a148 100644 --- a/qutebrowser/commands/cmdutils.py +++ b/qutebrowser/commands/cmdutils.py @@ -91,7 +91,8 @@ class register: # pylint: disable=invalid-name much cleaner to implement. Attributes: - _instance: The instance to be used as "self", as a dotted string. + _instance: The object from the object registry to be used as "self". + _scope: The scope to get _instance for. _name: The name (as string) or names (as list) of the command. _split: Whether to split the arguments. _hide: Whether to hide the command or not. @@ -105,7 +106,7 @@ class register: # pylint: disable=invalid-name def __init__(self, instance=None, name=None, split=True, hide=False, completion=None, modes=None, not_modes=None, needs_js=False, - debug=False, ignore_args=False): + debug=False, ignore_args=False, scope='global'): """Save decorator arguments. Gets called on parse-time with the decorator arguments. @@ -120,6 +121,7 @@ class register: # pylint: disable=invalid-name self._split = split self._hide = hide self._instance = instance + self._scope = scope self._completion = completion self._modes = modes self._not_modes = not_modes @@ -179,10 +181,10 @@ class register: # pylint: disable=invalid-name raise ValueError("{} is already registered!".format(name)) cmd = command.Command( name=names[0], split=self._split, hide=self._hide, - instance=self._instance, completion=self._completion, - modes=self._modes, not_modes=self._not_modes, - needs_js=self._needs_js, is_debug=self._debug, - ignore_args=self._ignore_args, handler=func) + instance=self._instance, scope=self._scope, + completion=self._completion, modes=self._modes, + not_modes=self._not_modes, needs_js=self._needs_js, + is_debug=self._debug, ignore_args=self._ignore_args, handler=func) for name in names: cmd_dict[name] = cmd aliases += names[1:] diff --git a/qutebrowser/commands/command.py b/qutebrowser/commands/command.py index bcb77b907..b1bad6ada 100644 --- a/qutebrowser/commands/command.py +++ b/qutebrowser/commands/command.py @@ -49,6 +49,7 @@ class Command: _not_modes: The modes the command can not be executed in. _count: Whether the command supports a count, or not. _instance: The object to bind 'self' to. + _scope: The scope to get _instance for in the object registry. Class attributes: AnnotationInfo: Named tuple for info from an annotation. @@ -61,7 +62,7 @@ class Command: def __init__(self, name, split, hide, instance, completion, modes, not_modes, needs_js, is_debug, ignore_args, - handler): + handler, scope): # I really don't know how to solve this in a better way, I tried. # pylint: disable=too-many-arguments,too-many-locals self.name = name @@ -71,6 +72,7 @@ class Command: self.completion = completion self._modes = modes self._not_modes = not_modes + self._scope = scope self._needs_js = needs_js self.debug = is_debug self.ignore_args = ignore_args @@ -300,7 +302,7 @@ class Command: args: The positional argument list. Gets modified directly. """ assert param.kind == inspect.Parameter.POSITIONAL_OR_KEYWORD - obj = objreg.get(self._instance) + obj = objreg.get(self._instance, scope=self._scope) args.append(obj) def _get_count_arg(self, param, args, kwargs): From 4067b584ec4fac25af5a9893ce7549f5ce30b128 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 25 Sep 2014 07:43:14 +0200 Subject: [PATCH 73/79] Add a registry argument to objreg.register. --- qutebrowser/utils/objreg.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/qutebrowser/utils/objreg.py b/qutebrowser/utils/objreg.py index 4a425ff96..e6af97aea 100644 --- a/qutebrowser/utils/objreg.py +++ b/qutebrowser/utils/objreg.py @@ -102,7 +102,7 @@ def get(name, default=_UNSET, scope='global'): raise -def register(name, obj, update=False, scope='global'): +def register(name, obj, update=False, scope=None, registry=None): """Helper function to register an object. Args: @@ -110,7 +110,15 @@ def register(name, obj, update=False, scope='global'): obj: The object to register. update: If True, allows to update an already registered object. """ - reg = _get_registry(scope) + if scope is not None and registry is not None: + raise ValueError("scope ({}) and registry ({}) can't be given at the " + "same time!".format(scope, registry)) + if registry is not None: + reg = registry + else: + if scope is None: + scope = 'global' + reg = _get_registry(scope) if not update and name in reg: raise KeyError("Object '{}' is already registered ({})!".format( name, repr(reg[name]))) From e8ce45c440ad779ebb7031f952fb2ea6858d36e9 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 25 Sep 2014 07:43:50 +0200 Subject: [PATCH 74/79] Add a tab-scope object registry. --- qutebrowser/utils/objreg.py | 12 ++++++++++++ qutebrowser/widgets/webview.py | 3 +++ 2 files changed, 15 insertions(+) diff --git a/qutebrowser/utils/objreg.py b/qutebrowser/utils/objreg.py index e6af97aea..8b2e5a2b8 100644 --- a/qutebrowser/utils/objreg.py +++ b/qutebrowser/utils/objreg.py @@ -36,6 +36,13 @@ class UnsetObject: __slots__ = () +class RegistryUnavailableError(Exception): + + """Exception raised when a certain registry does not exist yet.""" + + pass + + _UNSET = UnsetObject() @@ -81,6 +88,11 @@ def _get_registry(scope): """Get the correct registry for a given scope.""" if scope == 'global': return global_registry + elif scope == 'tab': + widget = get('tabbed-browser').currentWidget() + if widget is None: + raise RegistryUnavailableError(scope) + return widget.registry else: raise ValueError("Invalid scope '{}'!".format(scope)) diff --git a/qutebrowser/widgets/webview.py b/qutebrowser/widgets/webview.py index decb87c65..4dce57f63 100644 --- a/qutebrowser/widgets/webview.py +++ b/qutebrowser/widgets/webview.py @@ -51,6 +51,7 @@ class WebView(QWebView): open_target: Where to open the next tab ("normal", "tab", "tab_bg") viewing_source: Whether the webview is currently displaying source code. + registry: The ObjectRegistry associated with this tab. _cur_url: The current URL (accessed via cur_url property). _has_ssl_errors: Whether SSL errors occured during loading. _zoom: A NeighborList with the zoom levels. @@ -89,6 +90,8 @@ class WebView(QWebView): self._cur_url = None self.cur_url = QUrl() self.progress = 0 + self.registry = objreg.ObjectRegistry() + objreg.register('webview', self, registry=self.registry) page = webpage.BrowserPage(self) self.setPage(page) self.hintmanager = hints.HintManager(self) From 78949a8c1bf6adfb2ef4b5e410cd95451842c454 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 25 Sep 2014 07:44:11 +0200 Subject: [PATCH 75/79] Use object registry for hintmanager. --- qutebrowser/app.py | 7 --- qutebrowser/browser/commands.py | 56 ++------------------ qutebrowser/browser/hints.py | 77 ++++++++++++++++++---------- qutebrowser/keyinput/modeparsers.py | 37 ++++++------- qutebrowser/widgets/tabbedbrowser.py | 21 -------- qutebrowser/widgets/webview.py | 7 +-- 6 files changed, 72 insertions(+), 133 deletions(-) diff --git a/qutebrowser/app.py b/qutebrowser/app.py index 19d5eec0e..a1ac4fb4f 100644 --- a/qutebrowser/app.py +++ b/qutebrowser/app.py @@ -404,13 +404,6 @@ class Application(QApplication): status.keystring.setText) tabs.got_cmd.connect(self._commandrunner.run_safely) - # hints - kp[utypes.KeyMode.hint].fire_hint.connect(tabs.fire_hint) - kp[utypes.KeyMode.hint].filter_hints.connect(tabs.filter_hints) - kp[utypes.KeyMode.hint].keystring_updated.connect(tabs.handle_hint_key) - tabs.hint_strings_updated.connect( - kp[utypes.KeyMode.hint].on_hint_strings_updated) - # messages message_bridge.s_error.connect(status.disp_error) message_bridge.s_info.connect(status.disp_temp_text) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index 27e1219a0..c692a5ec3 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -302,55 +302,6 @@ class CommandDispatcher: for _ in range(count): self._current_widget().go_forward() - @cmdutils.register(instance='command-dispatcher') - def hint(self, group=webelem.Group.all, target=hints.Target.normal, - *args: {'nargs': '*'}): - """Start hinting. - - Args: - group: The hinting mode to use. - - - `all`: All clickable elements. - - `links`: Only links. - - `images`: Only images. - - target: What to do with the selected element. - - - `normal`: Open the link in the current tab. - - `tab`: Open the link in a new tab. - - `tab-bg`: Open the link in a new background tab. - - `yank`: Yank the link to the clipboard. - - `yank-primary`: Yank the link to the primary selection. - - `fill`: Fill the commandline with the command given as - argument. - - `rapid`: Open the link in a new tab and stay in hinting mode. - - `download`: Download the link. - - `userscript`: Call an userscript with `$QUTE_URL` set to the - link. - - `spawn`: Spawn a command. - - *args: Arguments for spawn/userscript/fill. - - - With `spawn`: The executable and arguments to spawn. - `{hint-url}` will get replaced by the selected - URL. - - With `userscript`: The userscript to execute. - - With `fill`: The command to fill the statusbar with. - `{hint-url}` will get replaced by the selected - URL. - """ - widget = self._current_widget() - frame = widget.page().mainFrame() - if frame is None: - raise cmdexc.CommandError("No frame focused!") - widget.hintmanager.start(frame, self._current_url(), group, target, - *args) - - @cmdutils.register(instance='command-dispatcher', hide=True) - def follow_hint(self): - """Follow the currently selected hint.""" - self._current_widget().hintmanager.follow_hint() - def _navigate_incdec(self, url, tab, incdec): """Helper method for :navigate when `where' is increment/decrement. @@ -424,12 +375,11 @@ class CommandDispatcher: url = self._current_url() if frame is None: raise cmdexc.CommandError("No frame focused!") + hintmanager = objreg.get('hintmanager', scope='tab') if where == 'prev': - widget.hintmanager.follow_prevnext(frame, url, prev=True, - newtab=tab) + hintmanager.follow_prevnext(frame, url, prev=True, newtab=tab) elif where == 'next': - widget.hintmanager.follow_prevnext(frame, url, prev=False, - newtab=tab) + hintmanager.follow_prevnext(frame, url, prev=False, newtab=tab) elif where == 'up': self._navigate_up(url, tab) elif where in ('decrement', 'increment'): diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 875d1eb4a..851e8302f 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -30,9 +30,8 @@ from PyQt5.QtWidgets import QApplication from qutebrowser.config import config from qutebrowser.keyinput import modeman from qutebrowser.browser import webelem -from qutebrowser.commands import userscripts, cmdexc -from qutebrowser.utils import (usertypes, log, qtutils, message, objreg, - cmdutils) +from qutebrowser.commands import userscripts, cmdexc, cmdutils +from qutebrowser.utils import usertypes, log, qtutils, message, objreg ElemTuple = collections.namedtuple('ElemTuple', 'elem, label') @@ -96,13 +95,8 @@ class HintManager(QObject): _context: The HintContext for the current invocation. Signals: - hint_strings_updated: Emitted when the possible hint strings changed. - arg: A list of hint strings. mouse_event: Mouse event to be posted in the web view. arg: A QMouseEvent - openurl: Open a new URL - arg 0: URL to open as QUrl. - arg 1: True if it should be opened in a new tab, else False. set_open_target: Set a new target to open the links in. """ @@ -133,9 +127,7 @@ class HintManager(QObject): Target.spawn: "Spawn command via hint...", } - hint_strings_updated = pyqtSignal(list) mouse_event = pyqtSignal('QMouseEvent') - openurl = pyqtSignal('QUrl', bool) set_open_target = pyqtSignal(str) def __init__(self, parent=None): @@ -492,7 +484,8 @@ class HintManager(QObject): for e, string in zip(elems, strings): label = self._draw_label(e, string) self._context.elems[string] = ElemTuple(e, label) - self.hint_strings_updated.emit(strings) + keyparser = objreg.get('keyparsers')[usertypes.KeyMode.hint] + keyparser.update_bindings(strings) def follow_prevnext(self, frame, baseurl, prev=False, newtab=False): """Click a "previous"/"next" element on the page. @@ -511,31 +504,60 @@ class HintManager(QObject): if url is None: raise cmdexc.CommandError("No {} links found!".format( "prev" if prev else "forward")) - self.openurl.emit(url, newtab) + qtutils.ensure_valid(url) + if newtab: + objreg.get('tabbed-browser').tabopen(url, background=False) + else: + objreg.get('webview', scope='tab').openurl(url) - def start(self, mainframe, baseurl, group=webelem.Group.all, - target=Target.normal, *args): + @cmdutils.register(instance='hintmanager', scope='tab', name='hint') + def start(self, group=webelem.Group.all, target=Target.normal, + *args: {'nargs': '*'}): """Start hinting. Args: - mainframe: The main QWebFrame. - baseurl: URL of the current page. - group: Which group of elements to hint. - target: What to do with the link. See attribute docstring. - *args: Arguments for userscript/download + group: The hinting mode to use. - Emit: - hint_strings_updated: Emitted to update keypraser. + - `all`: All clickable elements. + - `links`: Only links. + - `images`: Only images. + + target: What to do with the selected element. + + - `normal`: Open the link in the current tab. + - `tab`: Open the link in a new tab. + - `tab-bg`: Open the link in a new background tab. + - `yank`: Yank the link to the clipboard. + - `yank-primary`: Yank the link to the primary selection. + - `fill`: Fill the commandline with the command given as + argument. + - `rapid`: Open the link in a new tab and stay in hinting mode. + - `download`: Download the link. + - `userscript`: Call an userscript with `$QUTE_URL` set to the + link. + - `spawn`: Spawn a command. + + *args: Arguments for spawn/userscript/fill. + + - With `spawn`: The executable and arguments to spawn. + `{hint-url}` will get replaced by the selected + URL. + - With `userscript`: The userscript to execute. + - With `fill`: The command to fill the statusbar with. + `{hint-url}` will get replaced by the selected + URL. """ - self._check_args(target, *args) + tabbed_browser = objreg.get('tabbed-browser') + widget = tabbed_browser.currentWidget() + if widget is None: + raise cmdexc.CommandError("No WebView available yet!") + mainframe = widget.page().mainFrame() if mainframe is None: - # This should never happen since we check frame before calling - # start. But since we had a bug where frame is None in - # on_mode_left, we are extra careful here. - raise ValueError("start() was called with frame=None") + raise cmdexc.CommandError("No frame focused!") + self._check_args(target, *args) self._context = HintContext() self._context.target = target - self._context.baseurl = baseurl + self._context.baseurl = tabbed_browser.current_url() self._context.frames = webelem.get_child_frames(mainframe) self._context.args = args self._init_elements(mainframe, group) @@ -634,6 +656,7 @@ class HintManager(QObject): if self._context.target != Target.rapid: modeman.maybe_leave(usertypes.KeyMode.hint, 'followed') + @cmdutils.register(instance='hintmanager', scope='tab', hide=True) def follow_hint(self): """Follow the currently selected hint.""" if not self._context.to_follow: diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index e22bcd801..7c0ea59bc 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -23,12 +23,12 @@ Module attributes: STARTCHARS: Possible chars for starting a commandline input. """ -from PyQt5.QtCore import pyqtSignal, Qt +from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt from qutebrowser.utils import message from qutebrowser.config import config from qutebrowser.keyinput import keyparser -from qutebrowser.utils import usertypes, log +from qutebrowser.utils import usertypes, log, objreg STARTCHARS = ":/?" @@ -80,25 +80,17 @@ class HintKeyParser(keyparser.CommandKeyParser): """KeyChainParser for hints. - Signals: - fire_hint: When a hint keybinding was completed. - Arg: the keystring/hint string pressed. - filter_hints: When the filter text changed. - Arg: the text to filter hints with. - Attributes: _filtertext: The text to filter with. _last_press: The nature of the last keypress, a LastPress member. """ - fire_hint = pyqtSignal(str) - filter_hints = pyqtSignal(str) - def __init__(self, parent=None): super().__init__(parent, supports_count=False, supports_chains=True) self._filtertext = '' self._last_press = LastPress.none self.read_config('hint') + self.keystring_updated.connect(self.on_keystring_updated) def _handle_special_key(self, e): """Override _handle_special_key to handle string filtering. @@ -112,11 +104,11 @@ class HintKeyParser(keyparser.CommandKeyParser): True if event has been handled, False otherwise. Emit: - filter_hints: Emitted when filter string has changed. keystring_updated: Emitted when keystring has been changed. """ log.keyboard.debug("Got special key 0x{:x} text {}".format( e.key(), e.text())) + hintmanager = objreg.get('hintmanager', scope='tab') if e.key() == Qt.Key_Backspace: log.keyboard.debug("Got backspace, mode {}, filtertext '{}', " "keystring '{}'".format(self._last_press, @@ -124,7 +116,7 @@ class HintKeyParser(keyparser.CommandKeyParser): self._keystring)) if self._last_press == LastPress.filtertext and self._filtertext: self._filtertext = self._filtertext[:-1] - self.filter_hints.emit(self._filtertext) + hintmanager.filter_hints(self._filtertext) return True elif self._last_press == LastPress.keystring and self._keystring: self._keystring = self._keystring[:-1] @@ -138,7 +130,7 @@ class HintKeyParser(keyparser.CommandKeyParser): return super()._handle_special_key(e) else: self._filtertext += e.text() - self.filter_hints.emit(self._filtertext) + hintmanager.filter_hints(self._filtertext) self._last_press = LastPress.filtertext return True @@ -167,24 +159,25 @@ class HintKeyParser(keyparser.CommandKeyParser): return self._handle_special_key(e) def execute(self, cmdstr, keytype, count=None): - """Handle a completed keychain. - - Emit: - fire_hint: Emitted if keytype is chain - """ + """Handle a completed keychain.""" if not isinstance(keytype, self.Type): raise TypeError("Type {} is no Type member!".format(keytype)) if keytype == self.Type.chain: - self.fire_hint.emit(cmdstr) + objreg.get('hintmanager', scope='tab').fire(cmdstr) else: # execute as command super().execute(cmdstr, keytype, count) - def on_hint_strings_updated(self, strings): - """Handler for HintManager's hint_strings_updated. + def update_bindings(self, strings): + """Update bindings when the hint strings changed. Args: strings: A list of hint strings. """ self.bindings = {s: s for s in strings} self._filtertext = '' + + @pyqtSlot(str) + def on_keystring_updated(self, keystr): + """Update hintmanager when the keystring was updated.""" + objreg.get('hintmanager', scope='tab').handle_partial_key(keystr) diff --git a/qutebrowser/widgets/tabbedbrowser.py b/qutebrowser/widgets/tabbedbrowser.py index f07443197..208d04a48 100644 --- a/qutebrowser/widgets/tabbedbrowser.py +++ b/qutebrowser/widgets/tabbedbrowser.py @@ -67,8 +67,6 @@ class TabbedBrowser(tabwidget.TabWidget): arg 1: x-position in %. arg 2: y-position in %. cur_load_status_changed: Loading status of current tab changed. - hint_strings_updated: Hint strings were updated. - arg: A list of hint strings. quit: The last tab was closed, quit application. resized: Emitted when the browser window has resized, so the completion widget can adjust its size to it. @@ -89,7 +87,6 @@ class TabbedBrowser(tabwidget.TabWidget): cur_scroll_perc_changed = pyqtSignal(int, int) cur_load_status_changed = pyqtSignal(str) start_download = pyqtSignal('QNetworkReply*') - hint_strings_updated = pyqtSignal(list) quit = pyqtSignal() resized = pyqtSignal('QRect') got_cmd = pyqtSignal(str) @@ -151,9 +148,6 @@ class TabbedBrowser(tabwidget.TabWidget): self._filter.create(self.cur_load_status_changed, tab)) tab.url_text_changed.connect( functools.partial(self.on_url_text_changed, tab)) - # hintmanager - tab.hintmanager.hint_strings_updated.connect(self.hint_strings_updated) - tab.hintmanager.openurl.connect(self.openurl) self.cur_load_started.connect(self.on_cur_load_started) # downloads page.start_download.connect(self.start_download) @@ -370,21 +364,6 @@ class TabbedBrowser(tabwidget.TabWidget): # We first want QWebPage to refresh. QTimer.singleShot(0, check_scroll_pos) - @pyqtSlot(str) - def handle_hint_key(self, keystr): - """Handle a new hint keypress.""" - self.currentWidget().hintmanager.handle_partial_key(keystr) - - @pyqtSlot(str) - def fire_hint(self, keystr): - """Fire a completed hint.""" - self.currentWidget().hintmanager.fire(keystr) - - @pyqtSlot(str) - def filter_hints(self, filterstr): - """Filter displayed hints.""" - self.currentWidget().hintmanager.filter_hints(filterstr) - @pyqtSlot(str, str) def on_config_changed(self, section, option): """Update tab config when config was changed.""" diff --git a/qutebrowser/widgets/webview.py b/qutebrowser/widgets/webview.py index 4dce57f63..e4ac1f1f3 100644 --- a/qutebrowser/widgets/webview.py +++ b/qutebrowser/widgets/webview.py @@ -94,9 +94,10 @@ class WebView(QWebView): objreg.register('webview', self, registry=self.registry) page = webpage.BrowserPage(self) self.setPage(page) - self.hintmanager = hints.HintManager(self) - self.hintmanager.mouse_event.connect(self.on_mouse_event) - self.hintmanager.set_open_target.connect(self.set_force_open_target) + hintmanager = hints.HintManager(self) + hintmanager.mouse_event.connect(self.on_mouse_event) + hintmanager.set_open_target.connect(self.set_force_open_target) + objreg.register('hintmanager', hintmanager, registry=self.registry) page.linkHovered.connect(self.linkHovered) page.mainFrame().loadStarted.connect(self.on_load_started) page.change_title.connect(self.titleChanged) From 1e7861660eb0fbf10ff274375efb69181db771bf Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 25 Sep 2014 07:45:30 +0200 Subject: [PATCH 76/79] Add 'meta' scope to objreg. --- qutebrowser/utils/objreg.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/qutebrowser/utils/objreg.py b/qutebrowser/utils/objreg.py index 8b2e5a2b8..5a798e58e 100644 --- a/qutebrowser/utils/objreg.py +++ b/qutebrowser/utils/objreg.py @@ -93,6 +93,8 @@ def _get_registry(scope): if widget is None: raise RegistryUnavailableError(scope) return widget.registry + elif scope == 'meta': + return meta_registry else: raise ValueError("Invalid scope '{}'!".format(scope)) From 143fdc5b9fccb5fa55e02422c2c1923dd487b558 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 25 Sep 2014 07:50:07 +0200 Subject: [PATCH 77/79] Register tab registry in meta registry. --- qutebrowser/utils/usertypes.py | 4 ++++ qutebrowser/widgets/webview.py | 2 ++ 2 files changed, 6 insertions(+) diff --git a/qutebrowser/utils/usertypes.py b/qutebrowser/utils/usertypes.py index ff0bbe572..7c480836c 100644 --- a/qutebrowser/utils/usertypes.py +++ b/qutebrowser/utils/usertypes.py @@ -23,6 +23,7 @@ Module attributes: _UNSET: Used as default argument in the constructor so default can be None. """ +import itertools import operator import collections.abc import enum as pyenum @@ -35,6 +36,9 @@ from qutebrowser.utils import log, qtutils _UNSET = object() +tab_id_gen = itertools.count(0) + + def enum(name, *items, start=1, is_int=False): """Factory for simple enumerations. diff --git a/qutebrowser/widgets/webview.py b/qutebrowser/widgets/webview.py index e4ac1f1f3..36fdfd3d4 100644 --- a/qutebrowser/widgets/webview.py +++ b/qutebrowser/widgets/webview.py @@ -98,6 +98,8 @@ class WebView(QWebView): hintmanager.mouse_event.connect(self.on_mouse_event) hintmanager.set_open_target.connect(self.set_force_open_target) objreg.register('hintmanager', hintmanager, registry=self.registry) + tab_id = next(usertypes.tab_id_gen) + objreg.register('tab-{}'.format(tab_id), self.registry, scope='meta') page.linkHovered.connect(self.linkHovered) page.mainFrame().loadStarted.connect(self.on_load_started) page.change_title.connect(self.titleChanged) From 55e2ccabf5483bbaf6661c4d2af22c1ee136d3d8 Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 25 Sep 2014 07:58:08 +0200 Subject: [PATCH 78/79] Fix lint --- qutebrowser/browser/commands.py | 2 +- qutebrowser/browser/hints.py | 2 +- qutebrowser/keyinput/modeparsers.py | 2 +- qutebrowser/utils/objreg.py | 1 - 4 files changed, 3 insertions(+), 4 deletions(-) diff --git a/qutebrowser/browser/commands.py b/qutebrowser/browser/commands.py index c692a5ec3..abdd53d71 100644 --- a/qutebrowser/browser/commands.py +++ b/qutebrowser/browser/commands.py @@ -36,7 +36,7 @@ import pygments.formatters from qutebrowser.commands import userscripts, cmdexc, cmdutils from qutebrowser.config import config -from qutebrowser.browser import hints, quickmarks, webelem +from qutebrowser.browser import quickmarks, webelem from qutebrowser.utils import (message, editor, usertypes, log, qtutils, urlutils, objreg) diff --git a/qutebrowser/browser/hints.py b/qutebrowser/browser/hints.py index 851e8302f..5e841134b 100644 --- a/qutebrowser/browser/hints.py +++ b/qutebrowser/browser/hints.py @@ -351,7 +351,7 @@ class HintManager(QObject): immediately=True) return qtutils.ensure_valid(url) - cmdreg.get('download-manager').get(url, elem.webFrame().page()) + objreg.get('download-manager').get(url, elem.webFrame().page()) def _call_userscript(self, url): """Call an userscript from a hint.""" diff --git a/qutebrowser/keyinput/modeparsers.py b/qutebrowser/keyinput/modeparsers.py index 7c0ea59bc..5a25a2cba 100644 --- a/qutebrowser/keyinput/modeparsers.py +++ b/qutebrowser/keyinput/modeparsers.py @@ -23,7 +23,7 @@ Module attributes: STARTCHARS: Possible chars for starting a commandline input. """ -from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt +from PyQt5.QtCore import pyqtSlot, Qt from qutebrowser.utils import message from qutebrowser.config import config diff --git a/qutebrowser/utils/objreg.py b/qutebrowser/utils/objreg.py index 5a798e58e..6f233ccf2 100644 --- a/qutebrowser/utils/objreg.py +++ b/qutebrowser/utils/objreg.py @@ -99,7 +99,6 @@ def _get_registry(scope): raise ValueError("Invalid scope '{}'!".format(scope)) - def get(name, default=_UNSET, scope='global'): """Helper function to get an object. From f860e4fa6ba030d309b20da85ba7c756872725cc Mon Sep 17 00:00:00 2001 From: Florian Bruhin Date: Thu, 25 Sep 2014 08:06:21 +0200 Subject: [PATCH 79/79] Update commands.asciidoc --- doc/help/commands.asciidoc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/doc/help/commands.asciidoc b/doc/help/commands.asciidoc index 3b6b40820..053fa6e78 100644 --- a/doc/help/commands.asciidoc +++ b/doc/help/commands.asciidoc @@ -669,7 +669,6 @@ These commands are mainly intended for debugging. They are hidden if qutebrowser |============== |Command|Description |<>|Print a list of all objects to the debug log. -|<>|Print a list of all widgets to debug log. |<>|Print LRU cache stats. |<>|Show the debugging console. |<>|Crash for debugging purposes. @@ -679,10 +678,6 @@ These commands are mainly intended for debugging. They are hidden if qutebrowser === debug-all-objects Print a list of all objects to the debug log. -[[debug-all-widgets]] -=== debug-all-widgets -Print a list of all widgets to debug log. - [[debug-cache-stats]] === debug-cache-stats Print LRU cache stats.