##// END OF EJS Templates
Adding more documentation to config.application related files.
Brian Granger -
Show More
@@ -0,0 +1,90 b''
1 """
2 Tests for IPython.config.application.Application
3
4 Authors:
5
6 * Brian Granger
7 """
8
9 #-----------------------------------------------------------------------------
10 # Copyright (C) 2008-2011 The IPython Development Team
11 #
12 # Distributed under the terms of the BSD License. The full license is in
13 # the file COPYING, distributed as part of this software.
14 #-----------------------------------------------------------------------------
15
16 #-----------------------------------------------------------------------------
17 # Imports
18 #-----------------------------------------------------------------------------
19
20 from unittest import TestCase
21
22 from IPython.config.configurable import Configurable
23
24 from IPython.config.application import (
25 Application
26 )
27
28 from IPython.utils.traitlets import (
29 Bool, Unicode, Int, Float, List
30 )
31
32 #-----------------------------------------------------------------------------
33 # Code
34 #-----------------------------------------------------------------------------
35
36 class Foo(Configurable):
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.")
41
42
43 class Bar(Configurable):
44
45 enabled = Bool(True, config=True, shortname="enabled", help="Enable bar.")
46
47
48 class MyApp(Application):
49
50 app_name = Unicode(u'myapp')
51 running = Bool(False, config=True, shortname="running",
52 help="Is the app running?")
53 classes = List([Bar, Foo])
54 config_file = Unicode(u'', config=True, shortname="config_file",
55 help="Load this config file")
56
57 def init_foo(self):
58 self.foo = Foo(config=self.config)
59
60 def init_bar(self):
61 self.bar = Bar(config=self.config)
62
63
64 class TestApplication(TestCase):
65
66 def test_basic(self):
67 app = MyApp()
68 self.assertEquals(app.app_name, u'myapp')
69 self.assertEquals(app.running, False)
70 self.assertEquals(app.classes, [MyApp,Bar,Foo])
71 self.assertEquals(app.config_file, u'')
72
73 def test_config(self):
74 app = MyApp()
75 app.parse_command_line(["i=10","Foo.j=10","enabled=False","log_level=0"])
76 config = app.config
77 self.assertEquals(config.Foo.i, 10)
78 self.assertEquals(config.Foo.j, 10)
79 self.assertEquals(config.Bar.enabled, False)
80 self.assertEquals(config.MyApp.log_level,0)
81
82 def test_config_propagation(self):
83 app = MyApp()
84 app.parse_command_line(["i=10","Foo.j=10","enabled=False","log_level=0"])
85 app.init_foo()
86 app.init_bar()
87 self.assertEquals(app.foo.i, 10)
88 self.assertEquals(app.foo.j, 10)
89 self.assertEquals(app.bar.enabled, False)
90
@@ -1,132 +1,134 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 A base class for a configurable application.
3 A base class for a configurable application.
4
4
5 Authors:
5 Authors:
6
6
7 * Brian Granger
7 * Brian Granger
8 """
8 """
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2008-2011 The IPython Development Team
11 # Copyright (C) 2008-2011 The IPython Development Team
12 #
12 #
13 # Distributed under the terms of the BSD License. The full license is in
13 # Distributed under the terms of the BSD License. The full license is in
14 # the file COPYING, distributed as part of this software.
14 # the file COPYING, distributed as part of this software.
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Imports
18 # Imports
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20
20
21 from copy import deepcopy
21 from copy import deepcopy
22 import logging
22 import logging
23 import sys
23 import sys
24
24
25 from IPython.config.configurable import SingletonConfigurable
25 from IPython.config.configurable import SingletonConfigurable
26 from IPython.utils.traitlets import (
26 from IPython.utils.traitlets import (
27 Unicode, List, Int, Enum
27 Unicode, List, Int, Enum
28 )
28 )
29 from IPython.config.loader import (
29 from IPython.config.loader import (
30 KeyValueConfigLoader, PyFileConfigLoader
30 KeyValueConfigLoader, PyFileConfigLoader
31 )
31 )
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Application class
34 # Application class
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37
37
38 class Application(SingletonConfigurable):
38 class Application(SingletonConfigurable):
39 """A singleton application with full configuration support."""
39
40
40 # The name of the application, will usually match the name of the command
41 # The name of the application, will usually match the name of the command
41 # line application
42 # line application
42 app_name = Unicode(u'application')
43 app_name = Unicode(u'application')
43
44
44 # The description of the application that is printed at the beginning
45 # The description of the application that is printed at the beginning
45 # of the help.
46 # of the help.
46 description = Unicode(u'This is an application.')
47 description = Unicode(u'This is an application.')
47
48
48 # A sequence of Configurable subclasses whose config=True attributes will
49 # A sequence of Configurable subclasses whose config=True attributes will
49 # be exposed at the command line (shortnames and help).
50 # be exposed at the command line (shortnames and help).
50 classes = List([])
51 classes = List([])
51
52
52 # The version string of this application.
53 # The version string of this application.
53 version = Unicode(u'0.0')
54 version = Unicode(u'0.0')
54
55
55 # The log level for the application
56 # The log level for the application
56 log_level = Enum((0,10,20,30,40,50), default_value=logging.WARN,
57 log_level = Enum((0,10,20,30,40,50), default_value=logging.WARN,
57 config=True, shortname="log_level",
58 config=True, shortname="log_level",
58 help="Set the log level (0,10,20,30,40,50).")
59 help="Set the log level (0,10,20,30,40,50).")
59
60
60 def __init__(self, **kwargs):
61 def __init__(self, **kwargs):
61 SingletonConfigurable.__init__(self, **kwargs)
62 SingletonConfigurable.__init__(self, **kwargs)
62 # Add my class to self.classes so my attributes appear in command line
63 # Add my class to self.classes so my attributes appear in command line
63 # options.
64 # options.
64 self.classes.insert(0, self.__class__)
65 self.classes.insert(0, self.__class__)
65 self.init_logging()
66 self.init_logging()
66
67
67 def init_logging(self):
68 def init_logging(self):
68 """Start logging for this application.
69 """Start logging for this application.
69
70
70 The default is to log to stdout using a StreaHandler. The log level
71 The default is to log to stdout using a StreaHandler. The log level
71 starts at loggin.WARN, but this can be adjusted by setting the
72 starts at loggin.WARN, but this can be adjusted by setting the
72 ``log_level`` attribute.
73 ``log_level`` attribute.
73 """
74 """
74 self.log = logging.getLogger(self.__class__.__name__)
75 self.log = logging.getLogger(self.__class__.__name__)
75 self.log.setLevel(self.log_level)
76 self.log.setLevel(self.log_level)
76 self._log_handler = logging.StreamHandler()
77 self._log_handler = logging.StreamHandler()
77 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
78 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
78 self._log_handler.setFormatter(self._log_formatter)
79 self._log_handler.setFormatter(self._log_formatter)
79 self.log.addHandler(self._log_handler)
80 self.log.addHandler(self._log_handler)
80
81
81 def _log_level_changed(self, name, old, new):
82 def _log_level_changed(self, name, old, new):
82 """Adjust the log level when log_level is set."""
83 """Adjust the log level when log_level is set."""
83 self.log.setLevel(new)
84 self.log.setLevel(new)
84
85
85 def print_help(self):
86 def print_help(self):
86 """Print the help for each Configurable class in self.classes."""
87 """Print the help for each Configurable class in self.classes."""
87 for cls in self.classes:
88 for cls in self.classes:
88 cls.class_print_help()
89 cls.class_print_help()
89 print
90 print
90
91
91 def print_description(self):
92 def print_description(self):
92 """Print the application description."""
93 """Print the application description."""
93 print self.description
94 print self.description
94 print
95 print
95
96
96 def print_version(self):
97 def print_version(self):
97 """Print the version string."""
98 """Print the version string."""
98 print self.version
99 print self.version
99
100
100 def update_config(self, config):
101 def update_config(self, config):
102 """Fire the traits events when the config is updated."""
101 # Save a copy of the current config.
103 # Save a copy of the current config.
102 newconfig = deepcopy(self.config)
104 newconfig = deepcopy(self.config)
103 # Merge the new config into the current one.
105 # Merge the new config into the current one.
104 newconfig._merge(config)
106 newconfig._merge(config)
105 # Save the combined config as self.config, which triggers the traits
107 # Save the combined config as self.config, which triggers the traits
106 # events.
108 # events.
107 self.config = config
109 self.config = config
108
110
109 def parse_command_line(self, argv=None):
111 def parse_command_line(self, argv=None):
110 """Parse the command line arguments."""
112 """Parse the command line arguments."""
111 argv = sys.argv[1:] if argv is None else argv
113 argv = sys.argv[1:] if argv is None else argv
112
114
113 if '-h' in argv or '--h' in argv:
115 if '-h' in argv or '--h' in argv:
114 self.print_description()
116 self.print_description()
115 self.print_help()
117 self.print_help()
116 sys.exit(1)
118 sys.exit(1)
117
119
118 if '--version' in argv:
120 if '--version' in argv:
119 self.print_version()
121 self.print_version()
120 sys.exit(1)
122 sys.exit(1)
121
123
122 loader = KeyValueConfigLoader(argv=argv, classes=self.classes)
124 loader = KeyValueConfigLoader(argv=argv, classes=self.classes)
123 config = loader.load_config()
125 config = loader.load_config()
124 self.update_config(config)
126 self.update_config(config)
125
127
126 def load_config_file(self, filename, path=None):
128 def load_config_file(self, filename, path=None):
127 """Load a .py based config file by filename and path."""
129 """Load a .py based config file by filename and path."""
128 # TODO: this raises IOError if filename does not exist.
130 # TODO: this raises IOError if filename does not exist.
129 loader = PyFileConfigLoader(filename, path=path)
131 loader = PyFileConfigLoader(filename, path=path)
130 config = loader.load_config()
132 config = loader.load_config()
131 self.update_config(config)
133 self.update_config(config)
132
134
@@ -1,61 +1,88 b''
1 """A simple example of how to use IPython.config.application.Application.
1 """A simple example of how to use IPython.config.application.Application.
2
2
3 This should serve as a simple example that shows how the IPython config
3 This should serve as a simple example that shows how the IPython config
4 system works. The main classes are:
4 system works. The main classes are:
5
5
6 * IPython.config.configurable.Configurable
6 * IPython.config.configurable.Configurable
7 * IPython.config.configurable.SingletonConfigurable
7 * IPython.config.configurable.SingletonConfigurable
8 * IPython.config.loader.Config
8 * IPython.config.loader.Config
9 * IPython.config.application.Application
9 * IPython.config.application.Application
10
11 To see the command line option help, run this program from the command line::
12
13 $ python appconfig.py -h
14
15 To make one of your classes configurable (from the command line and config
16 files) inherit from Configurable and declare class attributes as traits (see
17 classes Foo and Bar below). To make the traits configurable, you will need
18 to set the following options:
19
20 * ``config``: set to ``True`` to make the attribute configurable.
21 * ``shortname``: by default, configurable attributes are set using the syntax
22 "Classname.attributename". At the command line, this is a bit verbose, so
23 we allow "shortnames" to be declared. Setting a shortname is optional, but
24 when you do this, you can set the option at the command line using the
25 syntax: "shortname=value".
26 * ``help``: set the help string to display a help message when the ``-h``
27 option is given at the command line. The help string should be valid ReST.
28
29 When the config attribute of an Application is updated, it will fire all of
30 the trait's events for all of the config=True attributes.
10 """
31 """
11
32
12 import sys
33 import sys
13
34
14 from IPython.config.configurable import Configurable
35 from IPython.config.configurable import Configurable
15 from IPython.config.application import Application
36 from IPython.config.application import Application
16 from IPython.utils.traitlets import (
37 from IPython.utils.traitlets import (
17 Bool, Unicode, Int, Float, List
38 Bool, Unicode, Int, Float, List
18 )
39 )
19
40
41
20 class Foo(Configurable):
42 class Foo(Configurable):
43 """A class that has configurable, typed attributes.
44
45 """
21
46
22 i = Int(0, config=True, shortname='i', help="The integer i.")
47 i = Int(0, config=True, shortname='i', help="The integer i.")
23 j = Int(1, config=True, shortname='j', help="The integer j.")
48 j = Int(1, config=True, shortname='j', help="The integer j.")
24 name = Unicode(u'Brian', config=True, shortname='name', help="First name.")
49 name = Unicode(u'Brian', config=True, shortname='name', help="First name.")
25
50
26
51
27 class Bar(Configurable):
52 class Bar(Configurable):
28
53
29 enabled = Bool(True, config=True, shortname="enabled", help="Enable bar.")
54 enabled = Bool(True, config=True, shortname="enabled", help="Enable bar.")
30
55
31
56
32 class MyApp(Application):
57 class MyApp(Application):
33
58
34 app_name = Unicode(u'myapp')
59 app_name = Unicode(u'myapp')
35 running = Bool(False, config=True, shortname="running",
60 running = Bool(False, config=True, shortname="running",
36 help="Is the app running?")
61 help="Is the app running?")
37 classes = List([Bar, Foo])
62 classes = List([Bar, Foo])
38 config_file = Unicode(u'', config=True, shortname="config_file",
63 config_file = Unicode(u'', config=True, shortname="config_file",
39 help="Load this config file")
64 help="Load this config file")
40
65
41 def init_foo(self):
66 def init_foo(self):
67 # Pass config to other classes for them to inherit the config.
42 self.foo = Foo(config=self.config)
68 self.foo = Foo(config=self.config)
43
69
44 def init_bar(self):
70 def init_bar(self):
71 # Pass config to other classes for them to inherit the config.
45 self.bar = Bar(config=self.config)
72 self.bar = Bar(config=self.config)
46
73
47
74
48
75
49 def main():
76 def main():
50 app = MyApp()
77 app = MyApp()
51 app.parse_command_line()
78 app.parse_command_line()
52 if app.config_file:
79 if app.config_file:
53 app.load_config_file(app.config_file)
80 app.load_config_file(app.config_file)
54 app.init_foo()
81 app.init_foo()
55 app.init_bar()
82 app.init_bar()
56 print "app.config:"
83 print "app.config:"
57 print app.config
84 print app.config
58
85
59
86
60 if __name__ == "__main__":
87 if __name__ == "__main__":
61 main()
88 main()
General Comments 0
You need to be logged in to leave comments. Login now