Show More
@@ -24,6 +24,7 b' import os' | |||||
24 | import re |
|
24 | import re | |
25 | import sys |
|
25 | import sys | |
26 | from copy import deepcopy |
|
26 | from copy import deepcopy | |
|
27 | from collections import defaultdict | |||
27 |
|
28 | |||
28 | from IPython.config.configurable import SingletonConfigurable |
|
29 | from IPython.config.configurable import SingletonConfigurable | |
29 | from IPython.config.loader import ( |
|
30 | from IPython.config.loader import ( | |
@@ -329,6 +330,51 b' class Application(SingletonConfigurable):' | |||||
329 | self.subapp = subapp.instance() |
|
330 | self.subapp = subapp.instance() | |
330 | # and initialize subapp |
|
331 | # and initialize subapp | |
331 | self.subapp.initialize(argv) |
|
332 | self.subapp.initialize(argv) | |
|
333 | ||||
|
334 | def flatten_flags(self): | |||
|
335 | """flatten flags and aliases, so cl-args override as expected. | |||
|
336 | ||||
|
337 | This prevents issues such as an alias pointing to InteractiveShell, | |||
|
338 | but a config file setting the same trait in TerminalInteraciveShell | |||
|
339 | getting inappropriate priority over the command-line arg. | |||
|
340 | ||||
|
341 | Only aliases with exactly one descendent in the class list | |||
|
342 | will be promoted. | |||
|
343 | ||||
|
344 | """ | |||
|
345 | # build a tree of classes in our list that inherit from a particular | |||
|
346 | # it will be a dict by parent classname of classes in our list | |||
|
347 | # that are descendents | |||
|
348 | mro_tree = defaultdict(list) | |||
|
349 | for cls in self.classes: | |||
|
350 | clsname = cls.__name__ | |||
|
351 | for parent in cls.mro()[1:-3]: | |||
|
352 | # exclude cls itself and Configurable,HasTraits,object | |||
|
353 | mro_tree[parent.__name__].append(clsname) | |||
|
354 | # flatten aliases, which have the form: | |||
|
355 | # { 'alias' : 'Class.trait' } | |||
|
356 | aliases = {} | |||
|
357 | for alias, cls_trait in self.aliases.iteritems(): | |||
|
358 | cls,trait = cls_trait.split('.',1) | |||
|
359 | children = mro_tree[cls] | |||
|
360 | if len(children) == 1: | |||
|
361 | # exactly one descendent, promote alias | |||
|
362 | cls = children[0] | |||
|
363 | aliases[alias] = '.'.join([cls,trait]) | |||
|
364 | ||||
|
365 | # flatten flags, which are of the form: | |||
|
366 | # { 'key' : ({'Cls' : {'trait' : value}}, 'help')} | |||
|
367 | flags = {} | |||
|
368 | for key, (flagdict, help) in self.flags.iteritems(): | |||
|
369 | newflag = {} | |||
|
370 | for cls, subdict in flagdict.iteritems(): | |||
|
371 | children = mro_tree[cls] | |||
|
372 | # exactly one descendent, promote flag section | |||
|
373 | if len(children) == 1: | |||
|
374 | cls = children[0] | |||
|
375 | newflag[cls] = subdict | |||
|
376 | flags[key] = (newflag, help) | |||
|
377 | return flags, aliases | |||
332 |
|
378 | |||
333 | def parse_command_line(self, argv=None): |
|
379 | def parse_command_line(self, argv=None): | |
334 | """Parse the command line arguments.""" |
|
380 | """Parse the command line arguments.""" | |
@@ -350,9 +396,12 b' class Application(SingletonConfigurable):' | |||||
350 | if '--version' in argv: |
|
396 | if '--version' in argv: | |
351 | self.print_version() |
|
397 | self.print_version() | |
352 | self.exit(0) |
|
398 | self.exit(0) | |
|
399 | ||||
|
400 | # flatten flags&aliases, so cl-args get appropriate priority: | |||
|
401 | flags,aliases = self.flatten_flags() | |||
353 |
|
402 | |||
354 |
loader = KVArgParseConfigLoader(argv=argv, aliases= |
|
403 | loader = KVArgParseConfigLoader(argv=argv, aliases=aliases, | |
355 |
flags= |
|
404 | flags=flags) | |
356 | try: |
|
405 | try: | |
357 | config = loader.load_config() |
|
406 | config = loader.load_config() | |
358 | self.update_config(config) |
|
407 | self.update_config(config) |
@@ -17,9 +17,11 b' Authors:' | |||||
17 | # Imports |
|
17 | # Imports | |
18 | #----------------------------------------------------------------------------- |
|
18 | #----------------------------------------------------------------------------- | |
19 |
|
19 | |||
|
20 | import logging | |||
20 | from unittest import TestCase |
|
21 | from unittest import TestCase | |
21 |
|
22 | |||
22 | from IPython.config.configurable import Configurable |
|
23 | from IPython.config.configurable import Configurable | |
|
24 | from IPython.config.loader import Config | |||
23 |
|
25 | |||
24 | from IPython.config.application import ( |
|
26 | from IPython.config.application import ( | |
25 | Application |
|
27 | Application | |
@@ -60,11 +62,14 b' class MyApp(Application):' | |||||
60 | 'j' : 'Foo.j', |
|
62 | 'j' : 'Foo.j', | |
61 | 'name' : 'Foo.name', |
|
63 | 'name' : 'Foo.name', | |
62 | 'enabled' : 'Bar.enabled', |
|
64 | 'enabled' : 'Bar.enabled', | |
63 |
'log-level' : ' |
|
65 | 'log-level' : 'Application.log_level', | |
64 | }) |
|
66 | }) | |
65 |
|
67 | |||
66 | flags = Dict(dict(enable=({'Bar': {'enabled' : True}}, "Set Bar.enabled to True"), |
|
68 | flags = Dict(dict(enable=({'Bar': {'enabled' : True}}, "Set Bar.enabled to True"), | |
67 |
disable=({'Bar': {'enabled' : False}}, "Set Bar.enabled to False") |
|
69 | disable=({'Bar': {'enabled' : False}}, "Set Bar.enabled to False"), | |
|
70 | crit=({'Application' : {'log_level' : logging.CRITICAL}}, | |||
|
71 | "set level=CRITICAL"), | |||
|
72 | )) | |||
68 |
|
73 | |||
69 | def init_foo(self): |
|
74 | def init_foo(self): | |
70 | self.foo = Foo(config=self.config) |
|
75 | self.foo = Foo(config=self.config) | |
@@ -129,6 +134,30 b' class TestApplication(TestCase):' | |||||
129 | self.assertEquals(app.bar.enabled, True) |
|
134 | self.assertEquals(app.bar.enabled, True) | |
130 | self.assertEquals(app.bar.b, 10) |
|
135 | self.assertEquals(app.bar.b, 10) | |
131 |
|
136 | |||
|
137 | def test_flatten_flags(self): | |||
|
138 | cfg = Config() | |||
|
139 | cfg.MyApp.log_level = logging.WARN | |||
|
140 | app = MyApp() | |||
|
141 | app.update_config(cfg) | |||
|
142 | self.assertEquals(app.log_level, logging.WARN) | |||
|
143 | self.assertEquals(app.config.MyApp.log_level, logging.WARN) | |||
|
144 | app.initialize(["--crit"]) | |||
|
145 | self.assertEquals(app.log_level, logging.CRITICAL) | |||
|
146 | # this would be app.config.Application.log_level if it failed: | |||
|
147 | self.assertEquals(app.config.MyApp.log_level, logging.CRITICAL) | |||
|
148 | ||||
|
149 | def test_flatten_aliases(self): | |||
|
150 | cfg = Config() | |||
|
151 | cfg.MyApp.log_level = logging.WARN | |||
|
152 | app = MyApp() | |||
|
153 | app.update_config(cfg) | |||
|
154 | self.assertEquals(app.log_level, logging.WARN) | |||
|
155 | self.assertEquals(app.config.MyApp.log_level, logging.WARN) | |||
|
156 | app.initialize(["--log-level", "CRITICAL"]) | |||
|
157 | self.assertEquals(app.log_level, logging.CRITICAL) | |||
|
158 | # this would be app.config.Application.log_level if it failed: | |||
|
159 | self.assertEquals(app.config.MyApp.log_level, "CRITICAL") | |||
|
160 | ||||
132 | def test_extra_args(self): |
|
161 | def test_extra_args(self): | |
133 | app = MyApp() |
|
162 | app = MyApp() | |
134 | app.parse_command_line(["--Bar.b=5", 'extra', "--disable", 'args']) |
|
163 | app.parse_command_line(["--Bar.b=5", 'extra', "--disable", 'args']) |
@@ -374,12 +374,6 b' start_aliases.update(dict(' | |||||
374 | )) |
|
374 | )) | |
375 | start_aliases['clean-logs'] = 'IPClusterStart.clean_logs' |
|
375 | start_aliases['clean-logs'] = 'IPClusterStart.clean_logs' | |
376 |
|
376 | |||
377 | # set inherited Start keys directly, to ensure command-line args get higher priority |
|
|||
378 | # than config file options. |
|
|||
379 | for key,value in start_aliases.items(): |
|
|||
380 | if value.startswith('IPClusterEngines'): |
|
|||
381 | start_aliases[key] = value.replace('IPClusterEngines', 'IPClusterStart') |
|
|||
382 |
|
||||
383 | class IPClusterStart(IPClusterEngines): |
|
377 | class IPClusterStart(IPClusterEngines): | |
384 |
|
378 | |||
385 | name = u'ipcluster' |
|
379 | name = u'ipcluster' |
General Comments 0
You need to be logged in to leave comments.
Login now