##// END OF EJS Templates
move shortname to Application...
MinRK -
Show More
@@ -23,13 +23,15 b' import logging'
23 23 import sys
24 24
25 25 from IPython.config.configurable import SingletonConfigurable
26 from IPython.utils.traitlets import (
27 Unicode, List, Int, Enum
28 )
29 26 from IPython.config.loader import (
30 27 KeyValueConfigLoader, PyFileConfigLoader
31 28 )
32 29
30 from IPython.utils.traitlets import (
31 Unicode, List, Int, Enum, Dict
32 )
33 from IPython.utils.text import indent
34
33 35 #-----------------------------------------------------------------------------
34 36 # Application class
35 37 #-----------------------------------------------------------------------------
@@ -55,16 +57,33 b' class Application(SingletonConfigurable):'
55 57
56 58 # The log level for the application
57 59 log_level = Enum((0,10,20,30,40,50), default_value=logging.WARN,
58 config=True, shortname="log_level",
60 config=True,
59 61 help="Set the log level (0,10,20,30,40,50).")
62
63 # the shortname map for configurables
64 shortnames = Dict(dict(log_level='Application.log_level'))
65
66 # macros for loading Configurables or store_const style flags
67 # macros are loaded from this dict by '--key' flags
68 macros = Dict()
69 # macro_help dict keys must match macros
70 macro_help = Dict()
71
60 72
61 73 def __init__(self, **kwargs):
62 74 SingletonConfigurable.__init__(self, **kwargs)
63 75 # Add my class to self.classes so my attributes appear in command line
64 76 # options.
65 77 self.classes.insert(0, self.__class__)
78
79 # check that macro_help has the right keys
80 # there is probably a better way to do this that doesn't use 2 dicts
81 keys = set(self.macros.keys())
82 badkeys = keys.difference_update(set(self.macro_help.keys()))
83 if badkeys:
84 raise KeyError("macro %r has no help in `macro_help`!"%badkeys.pop())
66 85 self.init_logging()
67
86
68 87 def init_logging(self):
69 88 """Start logging for this application.
70 89
@@ -82,9 +101,45 b' class Application(SingletonConfigurable):'
82 101 def _log_level_changed(self, name, old, new):
83 102 """Adjust the log level when log_level is set."""
84 103 self.log.setLevel(new)
85
104
105 def print_shortname_help(self):
106 """print the shortname part of the help"""
107 if not self.shortnames:
108 return
109
110 print "Aliases"
111 print "-------"
112 classdict = {}
113 for c in self.classes:
114 classdict[c.__name__] = c
115 for shortname, longname in self.shortnames.iteritems():
116 classname, traitname = longname.split('.',1)
117 cls = classdict[classname]
118
119 trait = cls.class_traits(config=True)[traitname]
120 help = trait.get_metadata('help')
121 print shortname, "(%s)"%longname, ':', trait.__class__.__name__
122 if help:
123 print indent(help)
124 print
125
126 def print_macro_help(self):
127 """print the the macro part of the help"""
128 if not self.macros:
129 return
130
131 print "Flags"
132 print "-----"
133
134 for m, cfg in self.macros.iteritems():
135 print '--'+m
136 print indent(self.macro_help[m])
137 print
138
86 139 def print_help(self):
87 140 """Print the help for each Configurable class in self.classes."""
141 self.print_macro_help()
142 self.print_shortname_help()
88 143 for cls in self.classes:
89 144 cls.class_print_help()
90 145 print
@@ -112,7 +167,7 b' class Application(SingletonConfigurable):'
112 167 """Parse the command line arguments."""
113 168 argv = sys.argv[1:] if argv is None else argv
114 169
115 if '-h' in argv or '--h' in argv:
170 if '-h' in argv or '--help' in argv:
116 171 self.print_description()
117 172 self.print_help()
118 173 sys.exit(1)
@@ -121,7 +176,8 b' class Application(SingletonConfigurable):'
121 176 self.print_version()
122 177 sys.exit(1)
123 178
124 loader = KeyValueConfigLoader(argv=argv, classes=self.classes)
179 loader = KeyValueConfigLoader(argv=argv, shortnames=self.shortnames,
180 macros=self.macros)
125 181 config = loader.load_config()
126 182 self.update_config(config)
127 183
@@ -140,18 +140,6 b' class Configurable(HasTraits):'
140 140 setattr(self, k, deepcopy(config_value))
141 141
142 142 @classmethod
143 def class_get_shortnames(cls):
144 """Return the shortname to fullname dict for config=True traits."""
145 cls_traits = cls.class_traits(config=True)
146 shortnames = {}
147 for k, v in cls_traits.items():
148 shortname = v.get_metadata('shortname')
149 if shortname is not None:
150 longname = cls.__name__ + '.' + k
151 shortnames[shortname] = longname
152 return shortnames
153
154 @classmethod
155 143 def class_get_help(cls):
156 144 """Get the help string for this class in ReST format."""
157 145 cls_traits = cls.class_traits(config=True)
@@ -160,10 +148,7 b' class Configurable(HasTraits):'
160 148 final_help.append(len(final_help[0])*u'-')
161 149 for k, v in cls_traits.items():
162 150 help = v.get_metadata('help')
163 shortname = v.get_metadata('shortname')
164 151 header = "%s.%s : %s" % (cls.__name__, k, v.__class__.__name__)
165 if shortname is not None:
166 header += " (shortname=" + shortname + ")"
167 152 final_help.append(header)
168 153 if help is not None:
169 154 final_help.append(indent(help))
@@ -18,6 +18,7 b' Authors'
18 18 #-----------------------------------------------------------------------------
19 19
20 20 import __builtin__
21 import re
21 22 import sys
22 23
23 24 from IPython.external import argparse
@@ -303,6 +304,8 b' class CommandLineConfigLoader(ConfigLoader):'
303 304 here.
304 305 """
305 306
307 kv_pattern = re.compile(r'[A-Za-z]\w*(\.\w+)*\=.+')
308 macro_pattern = re.compile(r'\-\-\w+(\-\w)*')
306 309
307 310 class KeyValueConfigLoader(CommandLineConfigLoader):
308 311 """A config loader that loads key value pairs from the command line.
@@ -312,7 +315,7 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
312 315 ipython Global.profile="foo" InteractiveShell.autocall=False
313 316 """
314 317
315 def __init__(self, argv=None, classes=None):
318 def __init__(self, argv=None, shortnames=None, macros=None):
316 319 """Create a key value pair config loader.
317 320
318 321 Parameters
@@ -321,9 +324,14 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
321 324 A list that has the form of sys.argv[1:] which has unicode
322 325 elements of the form u"key=value". If this is None (default),
323 326 then sys.argv[1:] will be used.
324 classes : (list, tuple) of Configurables
325 A sequence of Configurable classes that will be used to map
326 shortnames to longnames.
327 shortnames : dict
328 A dict of aliases for configurable traits.
329 Keys are the short aliases, Values are the resolved trait.
330 Of the form: `{'shortname' : 'Configurable.trait'}`
331 macros : dict
332 A dict of macros, keyed by str name. Vaues can be Config objects,
333 dicts, or "key=value" strings. If Config or dict, when the macro
334 is triggered, The macro is loaded as `self.config.update(m)`.
327 335
328 336 Returns
329 337 -------
@@ -340,12 +348,11 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
340 348 """
341 349 if argv is None:
342 350 argv = sys.argv[1:]
343 if classes is None:
344 classes = ()
345 351 self.argv = argv
346 self.classes = classes
352 self.shortnames = shortnames or {}
353 self.macros = macros or {}
347 354
348 def load_config(self, argv=None, classes=None):
355 def load_config(self, argv=None, shortnames=None, macros=None):
349 356 """Parse the configuration and generate the Config object.
350 357
351 358 Parameters
@@ -354,37 +361,28 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
354 361 A list that has the form of sys.argv[1:] which has unicode
355 362 elements of the form u"key=value". If this is None (default),
356 363 then self.argv will be used.
357 classes : (list, tuple) of Configurables
358 A sequence of Configurable classes that will be used to map
359 shortnames to longnames.
364 shortnames : dict
365 A dict of aliases for configurable traits.
366 Keys are the short aliases, Values are the resolved trait.
367 Of the form: `{'shortname' : 'Configurable.trait'}`
368 macros : dict
369 A dict of macros, keyed by str name. Vaues can be Config objects,
370 dicts, or "key=value" strings. If Config or dict, when the macro
371 is triggered, The macro is loaded as `self.config.update(m)`.
360 372 """
361 373 from IPython.config.configurable import Configurable
362 374
363 375 self.clear()
364 376 if argv is None:
365 377 argv = self.argv
366 if classes is None:
367 classes = self.classes
368
369 # Create the mapping between shortnames and longnames.
370 shortnames = {}
371 for cls in classes:
372 if issubclass(cls, Configurable):
373 sn = cls.class_get_shortnames()
374 # Check for duplicate shortnames and raise if found.
375 for k, v in sn.items():
376 if k in shortnames:
377 raise KeyError(
378 'Duplicate shortname: both %s and %s use the shortname: "%s"' %\
379 (v, shortnames[k], k)
380 )
381 shortnames.update(sn)
378 if shortnames is None:
379 shortnames = self.shortnames
380 if macros is None:
381 macros = self.macros
382 382
383 383 for item in argv:
384 pair = tuple(item.split("="))
385 if len(pair) == 2:
386 lhs = pair[0]
387 rhs = pair[1]
384 if kv_pattern.match(item):
385 lhs,rhs = item.split('=',1)
388 386 # Substitute longnames for shortnames.
389 387 if lhs in shortnames:
390 388 lhs = shortnames[lhs]
@@ -400,9 +398,26 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
400 398 # it succeeds. If it still fails, we let it raise.
401 399 exec_str = 'self.config.' + lhs + '="' + rhs + '"'
402 400 exec exec_str in locals(), globals()
401 elif macro_pattern.match(item):
402 # trim leading '--'
403 m = item[2:]
404 macro = macros.get(m, None)
405 if macro is None:
406 raise ValueError("Unrecognized argument: %r"%item)
407 macro = macros[m]
408 if isinstance(macro, basestring):
409 # macro is simply a 'Class.trait=value' string
410 exec_str = 'self.config.' + macro
411 exec exec_str in locals(), globals()
412 elif isinstance(macro, (dict, Configurable)):
413 # update self.config with Config:
414 self.config.update(macros[m])
415 else:
416 raise ValueError("Invalid macro: %r"%macro)
417 else:
418 raise ValueError("Invalid argument: %r"%item)
403 419 return self.config
404 420
405
406 421 class ArgParseConfigLoader(CommandLineConfigLoader):
407 422 """A loader that uses the argparse module to load from the command line."""
408 423
@@ -35,14 +35,14 b' from IPython.utils.traitlets import ('
35 35
36 36 class Foo(Configurable):
37 37
38 i = Int(0, config=True, shortname='i', help="The integer i.")
39 j = Int(1, config=True, shortname='j', help="The integer j.")
40 name = Unicode(u'Brian', config=True, shortname='name', help="First name.")
38 i = Int(0, config=True, help="The integer i.")
39 j = Int(1, config=True, help="The integer j.")
40 name = Unicode(u'Brian', config=True, help="First name.")
41 41
42 42
43 43 class Bar(Configurable):
44 44
45 enabled = Bool(True, config=True, shortname="enabled", help="Enable bar.")
45 enabled = Bool(True, config=True, help="Enable bar.")
46 46
47 47
48 48 class MyApp(Application):
@@ -54,6 +54,15 b' class MyApp(Application):'
54 54 config_file = Unicode(u'', config=True, shortname="config_file",
55 55 help="Load this config file")
56 56
57 shortnames = dict(i='Foo.i',j='Foo.j',name='Foo.name',
58 enabled='Bar.enabled', log_level='MyApp.log_level')
59
60 macros = dict(enable='Bar.enabled=True', disable='Bar.enabled=False')
61
62 macro_help = dict(
63 enable="""Enable bar""",
64 disable="""Disable bar"""
65 )
57 66 def init_foo(self):
58 67 self.foo = Foo(config=self.config)
59 68
@@ -88,3 +97,12 b' class TestApplication(TestCase):'
88 97 self.assertEquals(app.foo.j, 10)
89 98 self.assertEquals(app.bar.enabled, False)
90 99
100 def test_macro(self):
101 app = MyApp()
102 app.parse_command_line(["--disable"])
103 app.init_bar()
104 self.assertEquals(app.bar.enabled, False)
105 app.parse_command_line(["--enable"])
106 app.init_bar()
107 self.assertEquals(app.bar.enabled, True)
108
@@ -40,26 +40,26 b' from IPython.config.loader import Config'
40 40
41 41
42 42 class MyConfigurable(Configurable):
43 a = Int(1, config=True, shortname="a", help="The integer a.")
44 b = Float(1.0, config=True, shortname="b", help="The integer b.")
43 a = Int(1, config=True, help="The integer a.")
44 b = Float(1.0, config=True, help="The integer b.")
45 45 c = Str('no config')
46 46
47 47
48 48 mc_help=u"""MyConfigurable options
49 49 ----------------------
50 MyConfigurable.a : Int (shortname=a)
50 MyConfigurable.a : Int
51 51 The integer a.
52 MyConfigurable.b : Float (shortname=b)
52 MyConfigurable.b : Float
53 53 The integer b."""
54 54
55 55 class Foo(Configurable):
56 a = Int(0, config=True, shortname="a", help="The integer a.")
56 a = Int(0, config=True, help="The integer a.")
57 57 b = Str('nope', config=True)
58 58
59 59
60 60 class Bar(Foo):
61 b = Str('gotit', config=False, shortname="b", help="The string b.")
62 c = Float(config=True, shortname="c", help="The string c.")
61 b = Str('gotit', config=False, help="The string b.")
62 c = Float(config=True, help="The string c.")
63 63
64 64
65 65 class TestConfigurable(TestCase):
@@ -135,14 +135,6 b' class TestConfigurable(TestCase):'
135 135 self.assertEquals(c.b, 'and')
136 136 self.assertEquals(c.c, 20.0)
137 137
138 def test_shortnames(self):
139 sn = MyConfigurable.class_get_shortnames()
140 self.assertEquals(sn, {'a': 'MyConfigurable.a', 'b': 'MyConfigurable.b'})
141 sn = Foo.class_get_shortnames()
142 self.assertEquals(sn, {'a': 'Foo.a'})
143 sn = Bar.class_get_shortnames()
144 self.assertEquals(sn, {'a': 'Bar.a', 'c': 'Bar.c'})
145
146 138 def test_help(self):
147 139 self.assertEquals(MyConfigurable.class_get_help(), mc_help)
148 140
@@ -124,23 +124,6 b' class TestKeyValueCL(TestCase):'
124 124 self.assertEquals(config.Foo.Bam.value, range(10))
125 125 self.assertEquals(config.D.C.value, 'hi there')
126 126
127 def test_shortname(self):
128 class Foo(Configurable):
129 i = Int(0, config=True, shortname="i")
130 s = Unicode('hi', config=True, shortname="s")
131 cl = KeyValueConfigLoader()
132 config = cl.load_config(["i=20", "s=there"], classes=[Foo])
133 self.assertEquals(config.Foo.i, 20)
134 self.assertEquals(config.Foo.s, "there")
135
136 def test_duplicate(self):
137 class Foo(Configurable):
138 i = Int(0, config=True, shortname="i")
139 class Bar(Configurable):
140 i = Int(0, config=True, shortname="i")
141 cl = KeyValueConfigLoader()
142 self.assertRaises(KeyError, cl.load_config, ["i=20", "s=there"], classes=[Foo, Bar])
143
144 127
145 128 class TestConfig(TestCase):
146 129
@@ -62,6 +62,15 b' class MyApp(Application):'
62 62 classes = List([Bar, Foo])
63 63 config_file = Unicode(u'', config=True, shortname="config_file",
64 64 help="Load this config file")
65
66 shortnames = dict(i='Foo.i',j='Foo.j',name='Foo.name',
67 enabled='Bar.enabled')
68
69 macros = dict(enable='Bar.enabled=True', disable='Bar.enabled=False')
70 macro_help = dict(
71 enable="""Set Bar.enabled to True""",
72 disable="""Set Bar.enabled to False"""
73 )
65 74
66 75 def init_foo(self):
67 76 # Pass config to other classes for them to inherit the config.
General Comments 0
You need to be logged in to leave comments. Login now