From 716f7617c48ec3d4a62fe2c77b252bd7dfb6bec3 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 10 Apr 2013 08:52:33 +0400 Subject: [PATCH 1/9] Return exception string in place of throwing an exception Big bunch of `AttributeError`s when renderer failed to be created that renders vim unusable in case of some of the errors (e.g. invalid configuration) is annoying. Conflicts: powerline/__init__.py --- powerline/__init__.py | 55 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 1689f1da..4192bb27 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -10,10 +10,43 @@ from powerline.lib.config import ConfigLoader from threading import Lock, Event +try: + from __builtin__ import unicode +except ImportError: + unicode = str # NOQA + DEFAULT_SYSTEM_CONFIG_DIR = None +def safe_unicode(s): + '''Return unicode instance without raising an exception. + ''' + try: + try: + return unicode(s) + except UnicodeDecodeError: + try: + return unicode(s, 'utf-8') + except TypeError: + return unicode(str(s), 'utf-8') + except Exception as e: + return safe_unicode(e) + + +class FailedUnicode(unicode): + '''Builtin ``unicode`` (``str`` in python 3) subclass indicating fatal + error. + + If your code for some reason wants to determine whether `.render()` method + failed it should check returned string for being a FailedUnicode instance. + Alternatively you could subclass Powerline and override `.render()` method + to do what you like in place of catching the exception and returning + FailedUnicode. + ''' + pass + + def find_config_file(search_paths, config_file): config_file += '.json' for path in search_paths: @@ -360,16 +393,28 @@ class Powerline(object): try: self.create_renderer(**create_renderer_kwargs) except Exception as e: - self.pl.exception('Failed to create renderer: {0}', str(e)) - finally: - self.create_renderer_kwargs.clear() + if self.pl: + self.pl.exception('Failed to create renderer: {0}', str(e), prefix='powerline') + if hasattr(self, 'renderer'): + with self.cr_kwargs_lock: + self.create_renderer_kwargs.clear() + else: + raise + else: + with self.cr_kwargs_lock: + self.create_renderer_kwargs.clear() def render(self, *args, **kwargs): '''Update/create renderer if needed and pass all arguments further to ``self.renderer.render()``. ''' - self.update_renderer() - return self.renderer.render(*args, **kwargs) + try: + self.update_renderer() + return self.renderer.render(*args, **kwargs) + except Exception as e: + if self.pl: + self.pl.exception('Failed to render: {0}', str(e), prefix='powerline') + return FailedUnicode(safe_unicode(e)) def shutdown(self): '''Shut down all background threads. Must be run only prior to exiting From f389c43e4399d52395e6b88f693294c74d6276da Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 10 Apr 2013 18:09:34 +0400 Subject: [PATCH 2/9] Use `try/except KeyError` in place of `if in dict/else` --- powerline/segments/common.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/powerline/segments/common.py b/powerline/segments/common.py index 23ff2115..f0170156 100644 --- a/powerline/segments/common.py +++ b/powerline/segments/common.py @@ -697,13 +697,13 @@ class NetworkLoadSegment(KwThreadedSegment): total = activity interface = name - if interface in self.interfaces: + try: idata = self.interfaces[interface] try: idata['prev'] = idata['last'] except KeyError: pass - else: + except KeyError: idata = {} if self.run_once: idata['prev'] = (monotonic(), _get_bytes(interface)) From 201175d368f8fe4ddd1a3dae1ab22e929aad9331 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 10 Apr 2013 20:04:22 +0400 Subject: [PATCH 3/9] Pop path from self.loaded on exception --- powerline/lib/config.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/powerline/lib/config.py b/powerline/lib/config.py index 8fec4d95..3b8ce5a4 100644 --- a/powerline/lib/config.py +++ b/powerline/lib/config.py @@ -144,6 +144,10 @@ class ConfigLoader(MultiRunnedThread): self.loaded[path] = deepcopy(self._load(path)) except Exception as e: self.exception('Error while loading {0}: {1}', path, str(e)) + try: + self.loaded.pop(path) + except KeyError: + pass def run(self): while self.interval is not None and not self.shutdown_event.is_set(): From 82e2ea10c47ea4c904a79df19d7cb236d75693b2 Mon Sep 17 00:00:00 2001 From: ZyX Date: Tue, 23 Apr 2013 19:29:31 +0400 Subject: [PATCH 4/9] Replace self.pl.exception with self.exception New function is checking for self.pl being set before using logger and also removes the need of specifying explicit prefix="powerline". Conflicts: powerline/__init__.py --- powerline/__init__.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 4192bb27..b709bd92 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -266,7 +266,7 @@ class Powerline(object): try: Renderer = __import__(self.renderer_module, fromlist=['renderer']).renderer except Exception as e: - self.pl.exception('Failed to import renderer module: {0}', str(e)) + self.exception('Failed to import renderer module: {0}', str(e)) sys.exit(1) # Renderer updates configuration file via segments’ .startup thus it @@ -275,7 +275,7 @@ class Powerline(object): try: renderer = Renderer(**self.renderer_options) except Exception as e: - self.pl.exception('Failed to construct renderer object: {0}', str(e)) + self.exception('Failed to construct renderer object: {0}', str(e)) if not hasattr(self, 'renderer'): raise else: @@ -393,8 +393,7 @@ class Powerline(object): try: self.create_renderer(**create_renderer_kwargs) except Exception as e: - if self.pl: - self.pl.exception('Failed to create renderer: {0}', str(e), prefix='powerline') + self.exception('Failed to create renderer: {0}', str(e)) if hasattr(self, 'renderer'): with self.cr_kwargs_lock: self.create_renderer_kwargs.clear() @@ -412,8 +411,15 @@ class Powerline(object): self.update_renderer() return self.renderer.render(*args, **kwargs) except Exception as e: - if self.pl: - self.pl.exception('Failed to render: {0}', str(e), prefix='powerline') + try: + self.exception('Failed to render: {0}', str(e)) + except Exception as e: + # Updates e variable to new value, masking previous one. + # Normally it is the same exception (due to raise in case pl is + # unset), but it may also show error in logger. Note that latter + # is not logged by logger for obvious reasons, thus this also + # prevents us from seeing logger traceback. + pass return FailedUnicode(safe_unicode(e)) def shutdown(self): @@ -455,3 +461,9 @@ class Powerline(object): def __exit__(self, *args): self.shutdown() + + def exception(self, msg, prefix='powerline', *args, **kwargs): + if self.pl: + return self.pl.exception(msg, prefix=prefix, *args, **kwargs) + else: + raise From 1977a0125c50ade9fbeaff72eacc8f51842dc1da Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 1 May 2013 20:31:04 +0400 Subject: [PATCH 5/9] Remove cache before running self.exception, not after --- powerline/lib/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/powerline/lib/config.py b/powerline/lib/config.py index 3b8ce5a4..60aff1eb 100644 --- a/powerline/lib/config.py +++ b/powerline/lib/config.py @@ -143,11 +143,11 @@ class ConfigLoader(MultiRunnedThread): try: self.loaded[path] = deepcopy(self._load(path)) except Exception as e: - self.exception('Error while loading {0}: {1}', path, str(e)) try: self.loaded.pop(path) except KeyError: pass + self.exception('Error while loading {0}: {1}', path, str(e)) def run(self): while self.interval is not None and not self.shutdown_event.is_set(): From d882d312fcd0d4663212a099b726241e332de081 Mon Sep 17 00:00:00 2001 From: ZyX Date: Wed, 1 May 2013 21:18:08 +0400 Subject: [PATCH 6/9] Fix Powerline.exception --- powerline/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index b709bd92..0e03aaf1 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -462,8 +462,10 @@ class Powerline(object): def __exit__(self, *args): self.shutdown() - def exception(self, msg, prefix='powerline', *args, **kwargs): + def exception(self, msg, *args, **kwargs): + if 'prefix' not in kwargs: + kwargs['prefix'] = 'powerline' if self.pl: - return self.pl.exception(msg, prefix=prefix, *args, **kwargs) + return self.pl.exception(msg, *args, **kwargs) else: raise From 5bb32fadce5aa6e78f95a5051cb7eae2a78d051d Mon Sep 17 00:00:00 2001 From: ZyX Date: Thu, 23 May 2013 19:03:40 +0400 Subject: [PATCH 7/9] Use fallback logger if normal one is not available --- powerline/__init__.py | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 0e03aaf1..53ffd932 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -94,6 +94,29 @@ class PowerlineLogger(object): self._log('debug', msg, *args, **kwargs) +_fallback_logger = None + + +def _get_fallback_logger(): + global _fallback_logger + if _fallback_logger: + return _fallback_logger + + log_format = '%(asctime)s:%(levelname)s:%(message)s' + formatter = logging.Formatter(log_format) + + level = logging.WARNING + handler = logging.StreamHandler() + handler.setLevel(level) + handler.setFormatter(formatter) + + logger = logging.getLogger('powerline') + logger.setLevel(level) + logger.addHandler(handler) + _fallback_logger = PowerlineLogger(None, logger, '_fallback_') + return _fallback_logger + + class Powerline(object): '''Main powerline class, entrance point for all powerline uses. Sets powerline up and loads the configuration. @@ -465,7 +488,8 @@ class Powerline(object): def exception(self, msg, *args, **kwargs): if 'prefix' not in kwargs: kwargs['prefix'] = 'powerline' - if self.pl: + try: return self.pl.exception(msg, *args, **kwargs) - else: - raise + except AttributeError: + pl = _get_fallback_logger() + return pl.exception(msg, *args, **kwargs) From a06fe3ad5f52a3254df9fccb1683318447439c9c Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 17 Nov 2013 16:40:15 +0400 Subject: [PATCH 8/9] Do not double pl.exception() call, record used pl in variable instead --- powerline/__init__.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/powerline/__init__.py b/powerline/__init__.py index 53ffd932..629c5302 100644 --- a/powerline/__init__.py +++ b/powerline/__init__.py @@ -488,8 +488,5 @@ class Powerline(object): def exception(self, msg, *args, **kwargs): if 'prefix' not in kwargs: kwargs['prefix'] = 'powerline' - try: - return self.pl.exception(msg, *args, **kwargs) - except AttributeError: - pl = _get_fallback_logger() - return pl.exception(msg, *args, **kwargs) + pl = getattr(self, 'pl', None) or _get_fallback_logger() + return pl.exception(msg, *args, **kwargs) From bb343765b0a04493572e61426c52311d30f035bb Mon Sep 17 00:00:00 2001 From: ZyX Date: Sun, 26 Jan 2014 22:45:11 +0400 Subject: [PATCH 9/9] Add powerline: exception prefix --- tests/test_config_reload.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_config_reload.py b/tests/test_config_reload.py index b58eaff8..a98bfd1c 100644 --- a/tests/test_config_reload.py +++ b/tests/test_config_reload.py @@ -141,7 +141,7 @@ class TestConfigReload(TestCase): self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') self.assertAccessEvents('config', 'themes/test/nonexistent') # It should normally handle file missing error - self.assertEqual(p.logger._pop_msgs(), ['exception:test:Failed to create renderer: themes/test/nonexistent']) + self.assertEqual(p.logger._pop_msgs(), ['exception:test:powerline:Failed to create renderer: themes/test/nonexistent']) config['config']['ext']['test']['theme'] = 'default' add_watcher_events(p, 'config') @@ -154,7 +154,7 @@ class TestConfigReload(TestCase): self.assertEqual(p.render(), '<1 2 1> s <2 4 False>>><3 4 4>g <4 False False>>>') self.assertAccessEvents('config', 'colorschemes/test/nonexistent') # It should normally handle file missing error - self.assertEqual(p.logger._pop_msgs(), ['exception:test:Failed to create renderer: colorschemes/test/nonexistent']) + self.assertEqual(p.logger._pop_msgs(), ['exception:test:powerline:Failed to create renderer: colorschemes/test/nonexistent']) config['config']['ext']['test']['colorscheme'] = '2' add_watcher_events(p, 'config') @@ -187,7 +187,7 @@ class TestConfigReload(TestCase): add_watcher_events(p, 'config') self.assertEqual(p.render(), '<1 2 1> s<2 4 False>>><3 4 4>g<4 False False>>>') self.assertAccessEvents('config') - self.assertIn('exception:test:Failed to create renderer: fcf:colorschemes/test/nonexistentraise', p.logger._pop_msgs()) + self.assertIn('exception:test:powerline:Failed to create renderer: fcf:colorschemes/test/nonexistentraise', p.logger._pop_msgs()) config['colorschemes/test/nonexistentraise'] = { 'groups': {