From eaf1ae862cf7f6a1654d164d8526fb7d80528f4a Mon Sep 17 00:00:00 2001 From: Min RK Date: Tue, 1 Nov 2016 21:12:10 +0100 Subject: [PATCH] add Application.show_config[_json] setting this value will skip Application start and show loaded config on stdout, instead. if show_config[_json is given, it will be json-formatted --- traitlets/config/application.py | 97 ++++++++++++++++++++++++++++----- 1 file changed, 82 insertions(+), 15 deletions(-) diff --git a/traitlets/config/application.py b/traitlets/config/application.py index 708ed53d..732c363a 100644 --- a/traitlets/config/application.py +++ b/traitlets/config/application.py @@ -8,11 +8,16 @@ from collections import defaultdict, OrderedDict from copy import deepcopy +import io import json import logging import os +import pprint import re import sys + +from decorator import decorator + from traitlets.config.configurable import Configurable, SingletonConfigurable from traitlets.config.loader import ( KVArgParseConfigLoader, PyFileConfigLoader, Config, ArgumentError, ConfigFileNotFound, JSONFileConfigLoader @@ -236,23 +241,29 @@ def _log_default(self): #: the alias map for configurables #: Keys might strings or tuples for additional options; single-letter alias accessed like `-v`. #: Values might be like "Class.trait" strings of two-tuples: (Class.trait, help-text). - aliases = Dict({'log-level' : 'Application.log_level'}) + aliases = {'log-level' : 'Application.log_level'} # flags for loading Configurables or store_const style flags # flags are loaded from this dict by '--key' flags # this must be a dict of two-tuples, the first element being the Config/dict # and the second being the help string for the flag - flags = Dict() - @observe('flags') - @observe_compat - def _flags_changed(self, change): - """ensure flags dict is valid""" - new = change.new - for key, value in new.items(): - assert len(value) == 2, "Bad flag: %r:%s" % (key, value) - assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s" % (key, value) - assert isinstance(value[1], six.string_types), "Bad flag: %r:%s" % (key, value) - + flags = { + 'debug': { + 'Application': { + 'log_level': logging.DEBUG, + }, + }, + 'show-config': { + 'Application': { + 'show_config': True, + }, + }, + 'show-config-json': { + 'Application': { + 'show_config_json': True, + }, + }, + } # subcommands for launching other applications # if this is not empty, this will be a parent Application @@ -274,6 +285,25 @@ def _flags_changed(self, change): """ ) + _loaded_config_files = List() + + show_config = Bool( + help="Instead of starting the Application, dump configuration to stdout" + ).tag(config=True) + + show_config_json = Bool( + help="Instead of starting the Application, dump configuration to stdout (as JSON)" + ).tag(config=True) + + @observe('show_config_json') + def _show_config_json_changed(self, change): + self.show_config = change.new + + @observe('show_config') + def _show_config_changed(self, change): + if change.new: + self._save_start = self.start + self.start = self.start_show_config def __init__(self, **kwargs): SingletonConfigurable.__init__(self, **kwargs) @@ -311,6 +341,35 @@ def start(self): if self.subapp is not None: return self.subapp.start() + def start_show_config(self): + """start function used when show_config is True""" + if self.show_config_json: + json.dump(self.config, sys.stdout, + indent=1, sort_keys=True, default=repr) + return + + if self._loaded_config_files: + print("Loaded config files:") + for f in self._loaded_config_files: + print(' ' + f) + print() + + for classname in sorted(self.config): + print(classname) + class_config = self.config[classname] + if not class_config: + continue + for traitname in sorted(class_config): + value = class_config[traitname] + sio = io.StringIO() + pprint.pprint(value, stream=sio) + vs = sio.getvalue() + sys.stdout.write(' .%s = ' % traitname) + if '\n' in vs.strip(): + sys.stdout.write('\n') + vs = indent(vs, 4) + sys.stdout.write(vs) + def print_alias_help(self): """Print the alias part of the help.""" if not self.aliases: @@ -615,10 +674,10 @@ def _load_config_files(cls, basefilename, path=None, log=None, raise_config_file if log: log.debug("Looking for %s in %s", basefilename, path or os.getcwd()) jsonloader = cls.json_config_loader_class(basefilename+'.json', path=path, log=log) - config = None loaded = [] filenames = [] for loader in [pyloader, jsonloader]: + config = None try: config = loader.load_config() except ConfigFileNotFound: @@ -644,7 +703,7 @@ def _load_config_files(cls, basefilename, path=None, log=None, raise_config_file " {1} has higher priority: {2}".format( filename, loader.full_filename, json.dumps(collisions, indent=2), )) - yield config + yield (config, loader.full_filename) loaded.append(config) filenames.append(loader.full_filename) @@ -653,10 +712,11 @@ def load_config_file(self, filename, path=None): """Load config files by filename and path.""" filename, ext = os.path.splitext(filename) new_config = Config() - for config in self._load_config_files(filename, path=path, log=self.log, + for (config, filename) in self._load_config_files(filename, path=path, log=self.log, raise_config_file_errors=self.raise_config_file_errors, ): new_config.merge(config) + self._loaded_config_files.append(filename) # add self.cli_config to preserve CLI config priority new_config.merge(self.cli_config) self.update_config(new_config) @@ -727,6 +787,9 @@ def launch_instance(cls, argv=None, **kwargs): # utility functions, for convenience #----------------------------------------------------------------------------- +default_aliases = Application.aliases +default_flags = Application.flags + def boolean_flag(name, configurable, set_help='', unset_help=''): """Helper for building basic --trait, --no-trait flags. @@ -769,3 +832,7 @@ def get_config(): return Application.instance().config else: return Config() + + +if __name__ == '__main__': + Application.launch_instance()