From 7ed219c2f2118af984180a3eb6a72e19d4a3e59b 2011-10-22 18:35:20 From: MinRK Date: 2011-10-22 18:35:20 Subject: [PATCH] Show invalid config message on TraitErrors during initialization implemented via @catch_config decorator Now, the event that was triggered by invalid app config (see `--log-level 5`) is triggered by bad config at any point during initialization. This *will* catch TraitError bugs in IPython itself, but only during initialization. closes gh-908 --- diff --git a/IPython/config/application.py b/IPython/config/application.py index 90607ee..12abace 100644 --- a/IPython/config/application.py +++ b/IPython/config/application.py @@ -26,6 +26,8 @@ import sys from copy import deepcopy from collections import defaultdict +from IPython.external.decorator import decorator + from IPython.config.configurable import SingletonConfigurable from IPython.config.loader import ( KVArgParseConfigLoader, PyFileConfigLoader, Config, ArgumentError, ConfigFileNotFound, @@ -69,6 +71,26 @@ subcommand 'cmd', do: `{app} cmd -h`. # Application class #----------------------------------------------------------------------------- +@decorator +def catch_config(method, app, *args, **kwargs): + """Method decorator for catching invalid config (Trait/ArgumentErrors) during init. + + On a TraitError (generally caused by bad config), this will print the trait's + message, and exit the app. + + For use on init methods, to prevent invoking excepthook on invalid input. + """ + try: + return method(app, *args, **kwargs) + except (TraitError, ArgumentError) as e: + app.print_description() + app.print_help() + app.print_examples() + app.log.fatal("Bad config encountered during initialization:") + app.log.fatal(str(e)) + app.log.debug("Config at the time: %s", app.config) + app.exit(1) + class ApplicationError(Exception): pass @@ -173,6 +195,7 @@ class Application(SingletonConfigurable): self._log_handler.setFormatter(self._log_formatter) self.log.addHandler(self._log_handler) + @catch_config def initialize(self, argv=None): """Do the basic steps to configure me. @@ -317,6 +340,7 @@ class Application(SingletonConfigurable): # events. self.config = newconfig + @catch_config def initialize_subcommand(self, subc, argv=None): """Initialize a subcommand with argv.""" subapp,help = self.subcommands.get(subc) @@ -376,6 +400,7 @@ class Application(SingletonConfigurable): flags[key] = (newflag, help) return flags, aliases + @catch_config def parse_command_line(self, argv=None): """Parse the command line arguments.""" argv = sys.argv[1:] if argv is None else argv @@ -402,18 +427,12 @@ class Application(SingletonConfigurable): loader = KVArgParseConfigLoader(argv=argv, aliases=aliases, flags=flags) - try: - config = loader.load_config() - self.update_config(config) - except (TraitError, ArgumentError) as e: - self.print_description() - self.print_help() - self.print_examples() - self.log.fatal(str(e)) - self.exit(1) + config = loader.load_config() + self.update_config(config) # store unparsed args in extra_args self.extra_args = loader.extra_args + @catch_config def load_config_file(self, filename, path=None): """Load a .py based config file by filename and path.""" loader = PyFileConfigLoader(filename, path=path) diff --git a/IPython/core/application.py b/IPython/core/application.py index 5679aa7..24ee385 100644 --- a/IPython/core/application.py +++ b/IPython/core/application.py @@ -34,7 +34,7 @@ import os import shutil import sys -from IPython.config.application import Application +from IPython.config.application import Application, catch_config from IPython.config.configurable import Configurable from IPython.config.loader import Config, ConfigFileNotFound from IPython.core import release, crashhandler @@ -304,7 +304,7 @@ class BaseIPythonApplication(Application): with open(fname, 'w') as f: f.write(s) - + @catch_config def initialize(self, argv=None): # don't hook up crash handler before parsing command-line self.parse_command_line(argv) diff --git a/IPython/frontend/html/notebook/notebookapp.py b/IPython/frontend/html/notebook/notebookapp.py index d037f95..24f275d 100644 --- a/IPython/frontend/html/notebook/notebookapp.py +++ b/IPython/frontend/html/notebook/notebookapp.py @@ -43,6 +43,7 @@ from .handlers import (LoginHandler, ) from .notebookmanager import NotebookManager +from IPython.config.application import catch_config from IPython.core.application import BaseIPythonApplication from IPython.core.profiledir import ProfileDir from IPython.zmq.session import Session, default_secure @@ -260,6 +261,7 @@ class NotebookApp(BaseIPythonApplication): # and all of its ancenstors until propagate is set to False. self.log.propagate = False + @catch_config def initialize(self, argv=None): super(NotebookApp, self).initialize(argv) self.init_configurables() diff --git a/IPython/frontend/qt/console/qtconsoleapp.py b/IPython/frontend/qt/console/qtconsoleapp.py index 3cff36a..9783696 100644 --- a/IPython/frontend/qt/console/qtconsoleapp.py +++ b/IPython/frontend/qt/console/qtconsoleapp.py @@ -29,7 +29,7 @@ import uuid from IPython.external.qt import QtGui # Local imports -from IPython.config.application import boolean_flag +from IPython.config.application import boolean_flag, catch_config from IPython.core.application import BaseIPythonApplication from IPython.core.profiledir import ProfileDir from IPython.lib.kernel import tunnel_to_kernel, find_connection_file @@ -516,6 +516,7 @@ class IPythonQtConsoleApp(BaseIPythonApplication): else: raise IOError("Stylesheet %r not found."%self.stylesheet) + @catch_config def initialize(self, argv=None): super(IPythonQtConsoleApp, self).initialize(argv) self.init_connection_file() diff --git a/IPython/frontend/terminal/ipapp.py b/IPython/frontend/terminal/ipapp.py index aaa0594..f467198 100755 --- a/IPython/frontend/terminal/ipapp.py +++ b/IPython/frontend/terminal/ipapp.py @@ -32,7 +32,7 @@ import sys from IPython.config.loader import ( Config, PyFileConfigLoader, ConfigFileNotFound ) -from IPython.config.application import boolean_flag +from IPython.config.application import boolean_flag, catch_config from IPython.core import release from IPython.core import usage from IPython.core.completer import Completer @@ -285,7 +285,8 @@ class TerminalIPythonApp(BaseIPythonApplication, InteractiveShellApp): argv[idx] = sub return super(TerminalIPythonApp, self).parse_command_line(argv) - + + @catch_config def initialize(self, argv=None): """Do actions after construct, but before starting the app.""" super(TerminalIPythonApp, self).initialize(argv) diff --git a/IPython/parallel/apps/baseapp.py b/IPython/parallel/apps/baseapp.py index 86a8506..18eb28d 100644 --- a/IPython/parallel/apps/baseapp.py +++ b/IPython/parallel/apps/baseapp.py @@ -29,6 +29,7 @@ import sys from subprocess import Popen, PIPE +from IPython.config.application import catch_config from IPython.core import release from IPython.core.crashhandler import CrashHandler from IPython.core.application import ( @@ -144,6 +145,7 @@ class BaseParallelApplication(BaseIPythonApplication): aliases = Dict(base_aliases) flags = Dict(base_flags) + @catch_config def initialize(self, argv=None): """initialize the app""" super(BaseParallelApplication, self).initialize(argv) diff --git a/IPython/parallel/apps/ipclusterapp.py b/IPython/parallel/apps/ipclusterapp.py index a3b992b..ac0e729 100755 --- a/IPython/parallel/apps/ipclusterapp.py +++ b/IPython/parallel/apps/ipclusterapp.py @@ -31,7 +31,7 @@ from subprocess import check_call, CalledProcessError, PIPE import zmq from zmq.eventloop import ioloop -from IPython.config.application import Application, boolean_flag +from IPython.config.application import Application, boolean_flag, catch_config from IPython.config.loader import Config from IPython.core.application import BaseIPythonApplication from IPython.core.profiledir import ProfileDir @@ -269,6 +269,7 @@ class IPClusterEngines(BaseParallelApplication): flags = Dict(engine_flags) _stopping = False + @catch_config def initialize(self, argv=None): super(IPClusterEngines, self).initialize(argv) self.init_signal() diff --git a/IPython/parallel/apps/ipcontrollerapp.py b/IPython/parallel/apps/ipcontrollerapp.py index 90c3da8..339feaa 100755 --- a/IPython/parallel/apps/ipcontrollerapp.py +++ b/IPython/parallel/apps/ipcontrollerapp.py @@ -41,9 +41,10 @@ from IPython.parallel.apps.baseapp import ( BaseParallelApplication, base_aliases, base_flags, + catch_config, ) from IPython.utils.importstring import import_item -from IPython.utils.traitlets import Instance, Unicode, Bool, List, Dict +from IPython.utils.traitlets import Instance, Unicode, Bool, List, Dict, TraitError from IPython.zmq.session import ( Session, session_aliases, session_flags, default_secure @@ -263,7 +264,9 @@ class IPControllerApp(BaseParallelApplication): self.factory = HubFactory(config=c, log=self.log) # self.start_logging() self.factory.init_hub() - except: + except TraitError: + raise + except Exception: self.log.error("Couldn't construct the Controller", exc_info=True) self.exit(1) @@ -385,6 +388,7 @@ class IPControllerApp(BaseParallelApplication): self.log.addHandler(handler) self._log_handler = handler + @catch_config def initialize(self, argv=None): super(IPControllerApp, self).initialize(argv) self.forward_logging() diff --git a/IPython/parallel/apps/ipengineapp.py b/IPython/parallel/apps/ipengineapp.py index 413ea7e..647eb1c 100755 --- a/IPython/parallel/apps/ipengineapp.py +++ b/IPython/parallel/apps/ipengineapp.py @@ -34,6 +34,7 @@ from IPython.parallel.apps.baseapp import ( BaseParallelApplication, base_aliases, base_flags, + catch_config, ) from IPython.zmq.log import EnginePUBHandler from IPython.zmq.session import ( @@ -318,6 +319,7 @@ class IPEngineApp(BaseParallelApplication): else: mpi = None + @catch_config def initialize(self, argv=None): super(IPEngineApp, self).initialize(argv) self.init_mpi() diff --git a/IPython/parallel/apps/iploggerapp.py b/IPython/parallel/apps/iploggerapp.py index 430dfcf..36356e2 100755 --- a/IPython/parallel/apps/iploggerapp.py +++ b/IPython/parallel/apps/iploggerapp.py @@ -30,7 +30,8 @@ from IPython.utils.traitlets import Bool, Dict, Unicode from IPython.parallel.apps.baseapp import ( BaseParallelApplication, - base_aliases + base_aliases, + catch_config, ) from IPython.parallel.apps.logwatcher import LogWatcher @@ -68,6 +69,7 @@ class IPLoggerApp(BaseParallelApplication): classes = [LogWatcher, ProfileDir] aliases = Dict(aliases) + @catch_config def initialize(self, argv=None): super(IPLoggerApp, self).initialize(argv) self.init_watcher() diff --git a/IPython/zmq/ipkernel.py b/IPython/zmq/ipkernel.py index 2d138fd..b8caa2e 100755 --- a/IPython/zmq/ipkernel.py +++ b/IPython/zmq/ipkernel.py @@ -28,7 +28,7 @@ import zmq # Local imports. from IPython.config.configurable import Configurable -from IPython.config.application import boolean_flag +from IPython.config.application import boolean_flag, catch_config from IPython.core.application import ProfileDir from IPython.core.error import StdinNotImplementedError from IPython.core.shellapp import ( @@ -730,6 +730,8 @@ class IPKernelApp(KernelApp, InteractiveShellApp): selecting a particular matplotlib backend and loop integration. """ ) + + @catch_config def initialize(self, argv=None): super(IPKernelApp, self).initialize(argv) self.init_shell() diff --git a/IPython/zmq/kernelapp.py b/IPython/zmq/kernelapp.py index 2459281..94f94db 100644 --- a/IPython/zmq/kernelapp.py +++ b/IPython/zmq/kernelapp.py @@ -26,7 +26,7 @@ import zmq # IPython imports. from IPython.core.ultratb import FormattedTB from IPython.core.application import ( - BaseIPythonApplication, base_flags, base_aliases + BaseIPythonApplication, base_flags, base_aliases, catch_config ) from IPython.utils import io from IPython.utils.localinterfaces import LOCALHOST @@ -275,6 +275,7 @@ class KernelApp(BaseIPythonApplication): ) self.kernel.record_ports(self.ports) + @catch_config def initialize(self, argv=None): super(KernelApp, self).initialize(argv) self.init_blackhole()