##// END OF EJS Templates
Ongoing work on the config system....
Brian Granger -
Show More
@@ -0,0 +1,55 b''
1 import sys
2
3 from IPython.config.configurable import Configurable
4 from IPython.utils.traitlets import (
5 Bool, Unicode, Int, Float, List
6 )
7 from IPython.config.loader import KeyValueConfigLoader
8
9 class Foo(Configurable):
10
11 i = Int(0, config=True, shortname='i', help="The integer i.")
12 j = Int(1, config=True, shortname='j', help="The integer j.")
13 name = Unicode(u'Brian', config=True, shortname='name', help="First name.")
14
15
16 class Bar(Configurable):
17
18 enabled = Bool(True, config=True, shortname="bar-enabled", help="Enable bar.")
19
20
21 class MyApp(Configurable):
22
23 app_name = Unicode(u'myapp', config=True, shortname="myapp", help="The app name.")
24 running = Bool(False, config=True, shortname="running", help="Is the app running?")
25 classes = List([Bar, Foo])
26
27 def __init__(self, **kwargs):
28 Configurable.__init__(self, **kwargs)
29 self.classes.insert(0, self.__class__)
30
31 def print_help(self):
32 for cls in self.classes:
33 cls.class_print_help()
34 print
35
36 def parse_command_line(self, argv=None):
37 if argv is None:
38 argv = sys.argv[1:]
39 if '-h' in argv or '--h' in argv:
40 self.print_help()
41 sys.exit(1)
42 loader = KeyValueConfigLoader(argv=argv, classes=self.classes)
43 config = loader.load_config()
44 self.config = config
45
46
47 def main():
48 app = MyApp()
49 app.parse_command_line()
50 print "app.config:"
51 print app.config
52
53
54 if __name__ == "__main__":
55 main()
@@ -27,6 +27,7 b' from weakref import WeakValueDictionary'
27 from IPython.utils.importstring import import_item
27 from IPython.utils.importstring import import_item
28 from loader import Config
28 from loader import Config
29 from IPython.utils.traitlets import HasTraits, Instance
29 from IPython.utils.traitlets import HasTraits, Instance
30 from IPython.utils.text import indent
30
31
31
32
32 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
@@ -137,3 +138,33 b' class Configurable(HasTraits):'
137 # shared by all instances, effectively making it a class attribute.
138 # shared by all instances, effectively making it a class attribute.
138 setattr(self, k, deepcopy(config_value))
139 setattr(self, k, deepcopy(config_value))
139
140
141 @classmethod
142 def class_get_shortnames(cls):
143 """Return the shortname to fullname dict for config=True traits."""
144 cls_traits = cls.class_traits(config=True)
145 shortnames = {}
146 for k, v in cls_traits.items():
147 shortname = v.get_metadata('shortname')
148 if shortname is not None:
149 longname = cls.__name__ + '.' + k
150 shortnames[shortname] = longname
151 return shortnames
152
153 @classmethod
154 def class_get_help(cls):
155 cls_traits = cls.class_traits(config=True)
156 final_help = []
157 final_help.append('%s options' % cls.__name__)
158 final_help.append(len(final_help[0])*'-')
159 for k, v in cls_traits.items():
160 help = v.get_metadata('help')
161 final_help.append(k + " : " + v.__class__.__name__)
162 if help is not None:
163 final_help.append(indent(help))
164 return '\n'.join(final_help)
165
166 @classmethod
167 def class_print_help(cls):
168 print cls.class_get_help()
169
170 No newline at end of file
@@ -315,7 +315,7 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
315 ipython Global.profile="foo" InteractiveShell.autocall=False
315 ipython Global.profile="foo" InteractiveShell.autocall=False
316 """
316 """
317
317
318 def __init__(self, argv=None):
318 def __init__(self, argv=None, classes=None):
319 """Create a key value pair config loader.
319 """Create a key value pair config loader.
320
320
321 Parameters
321 Parameters
@@ -324,6 +324,9 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
324 A list that has the form of sys.argv[1:] which has unicode
324 A list that has the form of sys.argv[1:] which has unicode
325 elements of the form u"key=value". If this is None (default),
325 elements of the form u"key=value". If this is None (default),
326 then sys.argv[1:] will be used.
326 then sys.argv[1:] will be used.
327 class : (list, tuple) of Configurables
328 A sequence of Configurable classes that will be used to map
329 shortnames to longnames.
327
330
328 Returns
331 Returns
329 -------
332 -------
@@ -338,11 +341,14 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
338 >>> cl.load_config(["foo='bar'","A.name='brian'","B.number=0"])
341 >>> cl.load_config(["foo='bar'","A.name='brian'","B.number=0"])
339 {'A': {'name': 'brian'}, 'B': {'number': 0}, 'foo': 'bar'}
342 {'A': {'name': 'brian'}, 'B': {'number': 0}, 'foo': 'bar'}
340 """
343 """
341 if argv == None:
344 if argv is None:
342 argv = sys.argv[1:]
345 argv = sys.argv[1:]
346 if classes is None:
347 classes = ()
343 self.argv = argv
348 self.argv = argv
349 self.classes = classes
344
350
345 def load_config(self, argv=None):
351 def load_config(self, argv=None, classes=None):
346 """Parse the configuration and generate the Config object.
352 """Parse the configuration and generate the Config object.
347
353
348 Parameters
354 Parameters
@@ -350,16 +356,47 b' class KeyValueConfigLoader(CommandLineConfigLoader):'
350 argv : list, optional
356 argv : list, optional
351 A list that has the form of sys.argv[1:] which has unicode
357 A list that has the form of sys.argv[1:] which has unicode
352 elements of the form u"key=value". If this is None (default),
358 elements of the form u"key=value". If this is None (default),
353 then self.argv will be used.
359 then self.argv will be used.
360 class : (list, tuple) of Configurables
361 A sequence of Configurable classes that will be used to map
362 shortnames to longnames.
354 """
363 """
355 self.clear()
364 self.clear()
356 if argv is None:
365 if argv is None:
357 argv = self.argv
366 argv = self.argv
367 if classes is None:
368 classes = self.classes
369
370 # print argv
371
372 shortnames = {}
373 for cls in classes:
374 sn = cls.class_get_shortnames()
375 # Check for duplicate shortnames and raise if found.
376 for k, v in sn.items():
377 if k in shortnames:
378 raise KeyError(
379 "duplicate shortname: %s and %s both use the shortname: %s" %\
380 (v, shortnames[k], k)
381 )
382 shortnames.update(sn)
383
384 # print shortnames
385
358 for item in argv:
386 for item in argv:
359 pair = tuple(item.split("="))
387 pair = tuple(item.split("="))
360 if len(pair) == 2:
388 if len(pair) == 2:
361 exec_str = 'self.config.' + pair[0] + '=' + pair[1]
389 lhs = pair[0]
362 exec exec_str in locals(), globals()
390 rhs = pair[1]
391 # Substitute longnames for shortnames.
392 if lhs in shortnames:
393 lhs = shortnames[lhs]
394 exec_str = 'self.config.' + lhs + '=' + rhs
395 try:
396 exec exec_str in locals(), globals()
397 except (NameError, SyntaxError):
398 exec_str = 'self.config.' + lhs + '="' + rhs + '"'
399 exec exec_str in locals(), globals()
363 return self.config
400 return self.config
364
401
365
402
@@ -214,7 +214,7 b' class InteractiveShell(Configurable, Magic):'
214 # We can't do this yet because even runlines uses the autoindent.
214 # We can't do this yet because even runlines uses the autoindent.
215 autoindent = CBool(True, config=True, help=
215 autoindent = CBool(True, config=True, help=
216 """
216 """
217 Should IPython indent code entered interactively.
217 Autoindent IPython code entered interactively.
218 """
218 """
219 )
219 )
220 automagic = CBool(True, config=True, help=
220 automagic = CBool(True, config=True, help=
@@ -222,12 +222,37 b' class InteractiveShell(Configurable, Magic):'
222 Enable magic commands to be called without the leading %.
222 Enable magic commands to be called without the leading %.
223 """
223 """
224 )
224 )
225 cache_size = Int(1000, config=True)
225 cache_size = Int(1000, config=True, help=
226 color_info = CBool(True, config=True)
226 """
227 Set the size of the output cache. The default is 1000, you can
228 change it permanently in your config file. Setting it to 0 completely
229 disables the caching system, and the minimum value accepted is 20 (if
230 you provide a value less than 20, it is reset to 0 and a warning is
231 issued). This limit is defined because otherwise you'll spend more
232 time re-flushing a too small cache than working
233 """
234 )
235 color_info = CBool(True, config=True, help=
236 """
237 Use colors for displaying information about objects. Because this
238 information is passed through a pager (like 'less'), and some pagers
239 get confused with color codes, this capability can be turned off.
240 """
241 )
227 colors = CaselessStrEnum(('NoColor','LightBG','Linux'),
242 colors = CaselessStrEnum(('NoColor','LightBG','Linux'),
228 default_value=get_default_colors(), config=True)
243 default_value=get_default_colors(), config=True)
229 debug = CBool(False, config=True)
244 debug = CBool(False, config=True)
230 deep_reload = CBool(False, config=True)
245 deep_reload = CBool(False, config=True, help=
246 """
247 Enable deep (recursive) reloading by default. IPython can use the
248 deep_reload module which reloads changes in modules recursively (it
249 replaces the reload() function, so you don't need to change anything to
250 use it). deep_reload() forces a full reload of modules whose code may
251 have changed, which the default reload() function does not. When
252 deep_reload is off, IPython will use the normal reload(), but
253 deep_reload will still be available as dreload().
254 """
255 )
231 display_formatter = Instance(DisplayFormatter)
256 display_formatter = Instance(DisplayFormatter)
232 displayhook_class = Type(DisplayHook)
257 displayhook_class = Type(DisplayHook)
233 display_pub_class = Type(DisplayPublisher)
258 display_pub_class = Type(DisplayPublisher)
@@ -245,12 +270,28 b' class InteractiveShell(Configurable, Magic):'
245 # interactive statements or whole blocks.
270 # interactive statements or whole blocks.
246 input_splitter = Instance('IPython.core.inputsplitter.IPythonInputSplitter',
271 input_splitter = Instance('IPython.core.inputsplitter.IPythonInputSplitter',
247 (), {})
272 (), {})
248 logstart = CBool(False, config=True)
273 logstart = CBool(False, config=True, help=
249 logfile = Unicode('', config=True)
274 """
250 logappend = Unicode('', config=True)
275 Start logging to the default log file.
276 """
277 )
278 logfile = Unicode('', config=True, help=
279 """
280 The name of the logfile to use.
281 """
282 )
283 logappend = Unicode('', config=True, help=
284 """
285 Start logging to the given file in append mode.
286 """
287 )
251 object_info_string_level = Enum((0,1,2), default_value=0,
288 object_info_string_level = Enum((0,1,2), default_value=0,
252 config=True)
289 config=True)
253 pdb = CBool(False, config=True)
290 pdb = CBool(False, config=True, help=
291 """
292 Automatically call the pdb debugger after every exception.
293 """
294 )
254
295
255 profile = Unicode('', config=True)
296 profile = Unicode('', config=True)
256 prompt_in1 = Str('In [\\#]: ', config=True)
297 prompt_in1 = Str('In [\\#]: ', config=True)
@@ -366,6 +366,7 b' class TestHasTraits(TestCase):'
366 f = Float
366 f = Float
367 a = A()
367 a = A()
368 self.assertEquals(a.trait_names(),['i','f'])
368 self.assertEquals(a.trait_names(),['i','f'])
369 self.assertEquals(A.class_trait_names(),['i','f'])
369
370
370 def test_trait_metadata(self):
371 def test_trait_metadata(self):
371 class A(HasTraits):
372 class A(HasTraits):
@@ -379,6 +380,7 b' class TestHasTraits(TestCase):'
379 f = Float
380 f = Float
380 a = A()
381 a = A()
381 self.assertEquals(a.traits(), dict(i=A.i, f=A.f))
382 self.assertEquals(a.traits(), dict(i=A.i, f=A.f))
383 self.assertEquals(A.class_traits(), dict(i=A.i, f=A.f))
382
384
383 def test_traits_metadata(self):
385 def test_traits_metadata(self):
384 class A(HasTraits):
386 class A(HasTraits):
@@ -515,6 +515,48 b' class HasTraits(object):'
515 for n in names:
515 for n in names:
516 self._add_notifiers(handler, n)
516 self._add_notifiers(handler, n)
517
517
518 @classmethod
519 def class_trait_names(cls, **metadata):
520 """Get a list of all the names of this classes traits.
521
522 This method is just like the :meth:`trait_names` method, but is unbound.
523 """
524 return cls.class_traits(**metadata).keys()
525
526 @classmethod
527 def class_traits(cls, **metadata):
528 """Get a list of all the traits of this class.
529
530 This method is just like the :meth:`traits` method, but is unbound.
531
532 The TraitTypes returned don't know anything about the values
533 that the various HasTrait's instances are holding.
534
535 This follows the same algorithm as traits does and does not allow
536 for any simple way of specifying merely that a metadata name
537 exists, but has any value. This is because get_metadata returns
538 None if a metadata key doesn't exist.
539 """
540 traits = dict([memb for memb in getmembers(cls) if \
541 isinstance(memb[1], TraitType)])
542
543 if len(metadata) == 0:
544 return traits
545
546 for meta_name, meta_eval in metadata.items():
547 if type(meta_eval) is not FunctionType:
548 metadata[meta_name] = _SimpleTest(meta_eval)
549
550 result = {}
551 for name, trait in traits.items():
552 for meta_name, meta_eval in metadata.items():
553 if not meta_eval(trait.get_metadata(meta_name)):
554 break
555 else:
556 result[name] = trait
557
558 return result
559
518 def trait_names(self, **metadata):
560 def trait_names(self, **metadata):
519 """Get a list of all the names of this classes traits."""
561 """Get a list of all the names of this classes traits."""
520 return self.traits(**metadata).keys()
562 return self.traits(**metadata).keys()
General Comments 0
You need to be logged in to leave comments. Login now