Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

promote aliases and flags, to ensure they have priority over config files #855

Closed
wants to merge 1 commit into from

2 participants

@minrk
Owner

See #849

adds Application.flatten_flags() method, which adjusts the alias
and flag dicts, such that they point to the subclass in the Application.classes list when passed to the argv parser.

This prevents TerminalInteractiveShell.colors in a config file overriding
--colors on the command-line, which points to InteractiveShell.colors.

Flattening is only done when the answer is unambiguous, so multiply inherited classes (e.g. Launchers in ipcluster) are not touched.

also removes now-obsolete manual workaround for exactly this in IPClusterStart

closes gh-849

@minrk minrk promote aliases and flags, to ensure they have priority over config f…
…iles

add Application.flatten_flags() method, which adjusts the alias
and flag dicts, such that they point to the subclass in the Application.classes list when passed to the argv parser.

This prevents TerminalInteractiveShell.colors in a config file overriding
`--colors` on the command-line, which points to InteractiveShell.colors.

Flattening is only done when the answer is unambiguous, so multiply inherited classes (e.g. Launchers in ipcluster) are not touched.

also remove now-obsolete manual workaround for this in IPClusterStart

closes gh-849
d7a1059
@fperez
Owner

Great, thanks! Merged with rebase to avoid recursive merge for just one commit, closing.

@fperez fperez closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Oct 10, 2011
  1. @minrk

    promote aliases and flags, to ensure they have priority over config f…

    minrk authored
    …iles
    
    add Application.flatten_flags() method, which adjusts the alias
    and flag dicts, such that they point to the subclass in the Application.classes list when passed to the argv parser.
    
    This prevents TerminalInteractiveShell.colors in a config file overriding
    `--colors` on the command-line, which points to InteractiveShell.colors.
    
    Flattening is only done when the answer is unambiguous, so multiply inherited classes (e.g. Launchers in ipcluster) are not touched.
    
    also remove now-obsolete manual workaround for this in IPClusterStart
    
    closes gh-849
This page is out of date. Refresh to see the latest.
View
53 IPython/config/application.py
@@ -24,6 +24,7 @@
import re
import sys
from copy import deepcopy
+from collections import defaultdict
from IPython.config.configurable import SingletonConfigurable
from IPython.config.loader import (
@@ -329,6 +330,51 @@ def initialize_subcommand(self, subc, argv=None):
self.subapp = subapp.instance()
# and initialize subapp
self.subapp.initialize(argv)
+
+ def flatten_flags(self):
+ """flatten flags and aliases, so cl-args override as expected.
+
+ This prevents issues such as an alias pointing to InteractiveShell,
+ but a config file setting the same trait in TerminalInteraciveShell
+ getting inappropriate priority over the command-line arg.
+
+ Only aliases with exactly one descendent in the class list
+ will be promoted.
+
+ """
+ # build a tree of classes in our list that inherit from a particular
+ # it will be a dict by parent classname of classes in our list
+ # that are descendents
+ mro_tree = defaultdict(list)
+ for cls in self.classes:
+ clsname = cls.__name__
+ for parent in cls.mro()[1:-3]:
+ # exclude cls itself and Configurable,HasTraits,object
+ mro_tree[parent.__name__].append(clsname)
+ # flatten aliases, which have the form:
+ # { 'alias' : 'Class.trait' }
+ aliases = {}
+ for alias, cls_trait in self.aliases.iteritems():
+ cls,trait = cls_trait.split('.',1)
+ children = mro_tree[cls]
+ if len(children) == 1:
+ # exactly one descendent, promote alias
+ cls = children[0]
+ aliases[alias] = '.'.join([cls,trait])
+
+ # flatten flags, which are of the form:
+ # { 'key' : ({'Cls' : {'trait' : value}}, 'help')}
+ flags = {}
+ for key, (flagdict, help) in self.flags.iteritems():
+ newflag = {}
+ for cls, subdict in flagdict.iteritems():
+ children = mro_tree[cls]
+ # exactly one descendent, promote flag section
+ if len(children) == 1:
+ cls = children[0]
+ newflag[cls] = subdict
+ flags[key] = (newflag, help)
+ return flags, aliases
def parse_command_line(self, argv=None):
"""Parse the command line arguments."""
@@ -350,9 +396,12 @@ def parse_command_line(self, argv=None):
if '--version' in argv:
self.print_version()
self.exit(0)
+
+ # flatten flags&aliases, so cl-args get appropriate priority:
+ flags,aliases = self.flatten_flags()
- loader = KVArgParseConfigLoader(argv=argv, aliases=self.aliases,
- flags=self.flags)
+ loader = KVArgParseConfigLoader(argv=argv, aliases=aliases,
+ flags=flags)
try:
config = loader.load_config()
self.update_config(config)
View
33 IPython/config/tests/test_application.py
@@ -17,9 +17,11 @@
# Imports
#-----------------------------------------------------------------------------
+import logging
from unittest import TestCase
from IPython.config.configurable import Configurable
+from IPython.config.loader import Config
from IPython.config.application import (
Application
@@ -60,11 +62,14 @@ class MyApp(Application):
'j' : 'Foo.j',
'name' : 'Foo.name',
'enabled' : 'Bar.enabled',
- 'log-level' : 'MyApp.log_level',
+ 'log-level' : 'Application.log_level',
})
flags = Dict(dict(enable=({'Bar': {'enabled' : True}}, "Set Bar.enabled to True"),
- disable=({'Bar': {'enabled' : False}}, "Set Bar.enabled to False")))
+ disable=({'Bar': {'enabled' : False}}, "Set Bar.enabled to False"),
+ crit=({'Application' : {'log_level' : logging.CRITICAL}},
+ "set level=CRITICAL"),
+ ))
def init_foo(self):
self.foo = Foo(config=self.config)
@@ -129,6 +134,30 @@ def test_flag_clobber(self):
self.assertEquals(app.bar.enabled, True)
self.assertEquals(app.bar.b, 10)
+ def test_flatten_flags(self):
+ cfg = Config()
+ cfg.MyApp.log_level = logging.WARN
+ app = MyApp()
+ app.update_config(cfg)
+ self.assertEquals(app.log_level, logging.WARN)
+ self.assertEquals(app.config.MyApp.log_level, logging.WARN)
+ app.initialize(["--crit"])
+ self.assertEquals(app.log_level, logging.CRITICAL)
+ # this would be app.config.Application.log_level if it failed:
+ self.assertEquals(app.config.MyApp.log_level, logging.CRITICAL)
+
+ def test_flatten_aliases(self):
+ cfg = Config()
+ cfg.MyApp.log_level = logging.WARN
+ app = MyApp()
+ app.update_config(cfg)
+ self.assertEquals(app.log_level, logging.WARN)
+ self.assertEquals(app.config.MyApp.log_level, logging.WARN)
+ app.initialize(["--log-level", "CRITICAL"])
+ self.assertEquals(app.log_level, logging.CRITICAL)
+ # this would be app.config.Application.log_level if it failed:
+ self.assertEquals(app.config.MyApp.log_level, "CRITICAL")
+
def test_extra_args(self):
app = MyApp()
app.parse_command_line(["--Bar.b=5", 'extra', "--disable", 'args'])
View
6 IPython/parallel/apps/ipclusterapp.py
@@ -374,12 +374,6 @@ def start(self):
))
start_aliases['clean-logs'] = 'IPClusterStart.clean_logs'
-# set inherited Start keys directly, to ensure command-line args get higher priority
-# than config file options.
-for key,value in start_aliases.items():
- if value.startswith('IPClusterEngines'):
- start_aliases[key] = value.replace('IPClusterEngines', 'IPClusterStart')
-
class IPClusterStart(IPClusterEngines):
name = u'ipcluster'
Something went wrong with that request. Please try again.