##// END OF EJS Templates
Finished refactoring ipcontroller to be a proper application....
Brian Granger -
Show More
@@ -1,67 +1,72 b''
1 from IPython.config.loader import Config
1 from IPython.config.loader import Config
2
2
3 c = get_config()
3 c = get_config()
4
4
5 #-----------------------------------------------------------------------------
5 #-----------------------------------------------------------------------------
6 # Global configuration
6 # Global configuration
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8
8
9 c.Global.log_to_file = False
9 # Basic Global config attributes
10 c.Global.import_statements = []
10 # c.Global.log_to_file = False
11 c.Global.reuse_furls = False
11 # c.Global.import_statements = ['import math']
12 # c.Global.reuse_furls = True
13 # c.Global.secure = True
12
14
13 # You shouldn't have to edit these
15 # You shouldn't have to modify these
14 c.Global.log_dir_name = 'log'
16 # c.Global.log_dir_name = 'log'
15 c.Global.security_dir_name = 'security'
17 # c.Global.security_dir_name = 'security'
16
18
17
19
18 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
19 # Configure the client services
21 # Configure the client services
20 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
21
23
22 c.FCClientServiceFactory.ip = ''
24 # Basic client service config attributes
23 c.FCClientServiceFactory.port = 0
25 # c.FCClientServiceFactory.ip = ''
24 c.FCClientServiceFactory.location = ''
26 # c.FCClientServiceFactory.port = 0
25 c.FCClientServiceFactory.secure = True
27 # c.FCClientServiceFactory.location = ''
26 c.FCClientServiceFactory.reuse_furls = False
28 # c.FCClientServiceFactory.secure = True
27 c.FCClientServiceFactory.cert_file = 'ipcontroller-client.pem'
29 # c.FCClientServiceFactory.reuse_furls = False
28
30
29 c.FCClientServiceFactory.Interfaces.Task.interface_chain = [
31 # You shouldn't have to modify the rest of this section
30 'IPython.kernel.task.ITaskController',
32 # c.FCClientServiceFactory.cert_file = 'ipcontroller-client.pem'
31 'IPython.kernel.taskfc.IFCTaskController'
33
32 ]
34 # default_client_interfaces = Config()
33 # This is just the filename of the furl file. The path is always the
35 # default_client_interfaces.Task.interface_chain = [
34 # security dir of the cluster directory.
36 # 'IPython.kernel.task.ITaskController',
35 c.FCClientServiceFactory.Interfaces.Task.furl_file = 'ipcontroller-tc.furl'
37 # 'IPython.kernel.taskfc.IFCTaskController'
36
38 # ]
37 c.FCClientServiceFactory.Interfaces.MultiEngine.interface_chain = [
39 #
38 'IPython.kernel.multiengine.IMultiEngine',
40 # default_client_interfaces.Task.furl_file = 'ipcontroller-tc.furl'
39 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine'
41 #
40 ]
42 # default_client_interfaces.MultiEngine.interface_chain = [
41 # This is just the filename of the furl file. The path is always the
43 # 'IPython.kernel.multiengine.IMultiEngine',
42 # security dir of the cluster directory.
44 # 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine'
43 c.FCClientServiceFactory.Interfaces.MultiEngine.furl_file = 'ipcontroller-mec.furl'
45 # ]
44
46 #
47 # default_client_interfaces.MultiEngine.furl_file = 'ipcontroller-mec.furl'
48 #
49 # c.FCEngineServiceFactory.interfaces = default_client_interfaces
45
50
46 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
47 # Configure the engine services
52 # Configure the engine services
48 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
49
54
50 c.FCEngineServiceFactory.ip = ''
55 # Basic config attributes for the engine services
51 c.FCEngineServiceFactory.port = 0
56 # c.FCEngineServiceFactory.ip = ''
52 c.FCEngineServiceFactory.location = ''
57 # c.FCEngineServiceFactory.port = 0
53 c.FCEngineServiceFactory.secure = True
58 # c.FCEngineServiceFactory.location = ''
54 c.FCEngineServiceFactory.reuse_furls = False
59 # c.FCEngineServiceFactory.secure = True
55 c.FCEngineServiceFactory.cert_file = 'ipcontroller-engine.pem'
60 # c.FCEngineServiceFactory.reuse_furls = False
56
61
57 c.FCEngineServiceFactory.Intefaces.Default.interface_chain = [
62 # You shouldn't have to modify the rest of this section
58 'IPython.kernel.enginefc.IFCControllerBase'
63 # c.FCEngineServiceFactory.cert_file = 'ipcontroller-engine.pem'
59 ]
64
60
65 # default_engine_interfaces = Config()
61 # This is just the filename of the furl file. The path is always the
66 # default_engine_interfaces.Default.interface_chain = [
62 # security dir of the cluster directory.
67 # 'IPython.kernel.enginefc.IFCControllerBase'
63 c.FCEngineServiceFactory.Intefaces.Default.furl_file = 'ipcontroller-engine.furl'
68 # ]
64
69 #
65
70 # default_engine_interfaces.Default.furl_file = 'ipcontroller-engine.furl'
66
71 #
67
72 # c.FCEngineServiceFactory.interfaces = default_engine_interfaces
@@ -1,430 +1,452 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 An application for IPython
4 An application for IPython
5
5
6 Authors:
6 Authors:
7
7
8 * Brian Granger
8 * Brian Granger
9 * Fernando Perez
9 * Fernando Perez
10
10
11 Notes
11 Notes
12 -----
12 -----
13 """
13 """
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Copyright (C) 2008-2009 The IPython Development Team
16 # Copyright (C) 2008-2009 The IPython Development Team
17 #
17 #
18 # Distributed under the terms of the BSD License. The full license is in
18 # Distributed under the terms of the BSD License. The full license is in
19 # the file COPYING, distributed as part of this software.
19 # the file COPYING, distributed as part of this software.
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Imports
23 # Imports
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 import logging
26 import logging
27 import os
27 import os
28
28
29 import sys
29 import sys
30 import traceback
30 import traceback
31 from copy import deepcopy
31 from copy import deepcopy
32
32
33 from IPython.core import release
33 from IPython.core import release
34 from IPython.utils.genutils import get_ipython_dir, filefind
34 from IPython.utils.genutils import get_ipython_dir, filefind
35 from IPython.config.loader import (
35 from IPython.config.loader import (
36 PyFileConfigLoader,
36 PyFileConfigLoader,
37 ArgParseConfigLoader,
37 ArgParseConfigLoader,
38 Config,
38 Config,
39 NoConfigDefault
39 NoConfigDefault
40 )
40 )
41
41
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43 # Classes and functions
43 # Classes and functions
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45
45
46
46
47 class BaseAppArgParseConfigLoader(ArgParseConfigLoader):
47 class BaseAppArgParseConfigLoader(ArgParseConfigLoader):
48 """Default command line options for IPython based applications."""
48 """Default command line options for IPython based applications."""
49
49
50 def _add_other_arguments(self):
50 def _add_other_arguments(self):
51 self.parser.add_argument('-ipythondir', '--ipython-dir',
51 self.parser.add_argument('-ipythondir', '--ipython-dir',
52 dest='Global.ipythondir',type=str,
52 dest='Global.ipythondir',type=str,
53 help='Set to override default location of Global.ipythondir.',
53 help='Set to override default location of Global.ipythondir.',
54 default=NoConfigDefault,
54 default=NoConfigDefault,
55 metavar='Global.ipythondir')
55 metavar='Global.ipythondir')
56 self.parser.add_argument('-p','-profile', '--profile',
56 self.parser.add_argument('-p','-profile', '--profile',
57 dest='Global.profile',type=str,
57 dest='Global.profile',type=str,
58 help='The string name of the ipython profile to be used.',
58 help='The string name of the ipython profile to be used.',
59 default=NoConfigDefault,
59 default=NoConfigDefault,
60 metavar='Global.profile')
60 metavar='Global.profile')
61 self.parser.add_argument('-log_level', '--log-level',
61 self.parser.add_argument('-log_level', '--log-level',
62 dest="Global.log_level",type=int,
62 dest="Global.log_level",type=int,
63 help='Set the log level (0,10,20,30,40,50). Default is 30.',
63 help='Set the log level (0,10,20,30,40,50). Default is 30.',
64 default=NoConfigDefault)
64 default=NoConfigDefault)
65 self.parser.add_argument('-config_file', '--config-file',
65 self.parser.add_argument('-config_file', '--config-file',
66 dest='Global.config_file',type=str,
66 dest='Global.config_file',type=str,
67 help='Set the config file name to override default.',
67 help='Set the config file name to override default.',
68 default=NoConfigDefault,
68 default=NoConfigDefault,
69 metavar='Global.config_file')
69 metavar='Global.config_file')
70
70
71
71
72 class ApplicationError(Exception):
72 class ApplicationError(Exception):
73 pass
73 pass
74
74
75
75
76 class Application(object):
76 class Application(object):
77 """Load a config, construct an app and run it.
77 """Load a config, construct an app and run it.
78 """
78 """
79
79
80 config_file_name = 'ipython_config.py'
81 name = 'ipython'
80 name = 'ipython'
82 description = 'IPython: an enhanced interactive Python shell.'
81 description = 'IPython: an enhanced interactive Python shell.'
82 config_file_name = 'ipython_config.py'
83 default_log_level = logging.WARN
83 default_log_level = logging.WARN
84
84
85
85
86 def __init__(self):
86 def __init__(self):
87 self.init_logger()
87 self.init_logger()
88 self.default_config_file_name = self.config_file_name
88 self.default_config_file_name = self.config_file_name
89
89
90 def init_logger(self):
90 def init_logger(self):
91 self.log = logging.getLogger(self.__class__.__name__)
91 self.log = logging.getLogger(self.__class__.__name__)
92 # This is used as the default until the command line arguments are read.
92 # This is used as the default until the command line arguments are read.
93 self.log.setLevel(self.default_log_level)
93 self.log.setLevel(self.default_log_level)
94 self._log_handler = logging.StreamHandler()
94 self._log_handler = logging.StreamHandler()
95 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
95 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
96 self._log_handler.setFormatter(self._log_formatter)
96 self._log_handler.setFormatter(self._log_formatter)
97 self.log.addHandler(self._log_handler)
97 self.log.addHandler(self._log_handler)
98
98
99 def _set_log_level(self, level):
99 def _set_log_level(self, level):
100 self.log.setLevel(level)
100 self.log.setLevel(level)
101
101
102 def _get_log_level(self):
102 def _get_log_level(self):
103 return self.log.level
103 return self.log.level
104
104
105 log_level = property(_get_log_level, _set_log_level)
105 log_level = property(_get_log_level, _set_log_level)
106
106
107 def start(self):
107 def start(self):
108 """Start the application."""
108 """Start the application."""
109 self.attempt(self.create_default_config)
109 self.attempt(self.create_default_config)
110 self.log_default_config()
110 self.log_default_config()
111 self.set_default_config_log_level()
111 self.set_default_config_log_level()
112 self.attempt(self.pre_load_command_line_config)
112 self.attempt(self.pre_load_command_line_config)
113 self.attempt(self.load_command_line_config, action='abort')
113 self.attempt(self.load_command_line_config, action='abort')
114 self.set_command_line_config_log_level()
114 self.set_command_line_config_log_level()
115 self.attempt(self.post_load_command_line_config)
115 self.attempt(self.post_load_command_line_config)
116 self.log_command_line_config()
116 self.log_command_line_config()
117 self.attempt(self.find_ipythondir)
117 self.attempt(self.find_ipythondir)
118 self.attempt(self.find_config_file_name)
118 self.attempt(self.find_config_file_name)
119 self.attempt(self.find_config_file_paths)
119 self.attempt(self.find_config_file_paths)
120 self.attempt(self.pre_load_file_config)
120 self.attempt(self.pre_load_file_config)
121 self.attempt(self.load_file_config)
121 self.attempt(self.load_file_config)
122 self.set_file_config_log_level()
122 self.set_file_config_log_level()
123 self.attempt(self.post_load_file_config)
123 self.attempt(self.post_load_file_config)
124 self.log_file_config()
124 self.log_file_config()
125 self.attempt(self.merge_configs)
125 self.attempt(self.merge_configs)
126 self.log_master_config()
126 self.log_master_config()
127 self.attempt(self.pre_construct)
127 self.attempt(self.pre_construct)
128 self.attempt(self.construct)
128 self.attempt(self.construct)
129 self.attempt(self.post_construct)
129 self.attempt(self.post_construct)
130 self.attempt(self.start_app)
130 self.attempt(self.start_app)
131
131
132 #-------------------------------------------------------------------------
132 #-------------------------------------------------------------------------
133 # Various stages of Application creation
133 # Various stages of Application creation
134 #-------------------------------------------------------------------------
134 #-------------------------------------------------------------------------
135
135
136 def create_default_config(self):
136 def create_default_config(self):
137 """Create defaults that can't be set elsewhere.
137 """Create defaults that can't be set elsewhere.
138
138
139 For the most part, we try to set default in the class attributes
139 For the most part, we try to set default in the class attributes
140 of Components. But, defaults the top-level Application (which is
140 of Components. But, defaults the top-level Application (which is
141 not a HasTraitlets or Component) are not set in this way. Instead
141 not a HasTraitlets or Component) are not set in this way. Instead
142 we set them here. The Global section is for variables like this that
142 we set them here. The Global section is for variables like this that
143 don't belong to a particular component.
143 don't belong to a particular component.
144 """
144 """
145 self.default_config = Config()
145 self.default_config = Config()
146 self.default_config.Global.ipythondir = get_ipython_dir()
146 self.default_config.Global.ipythondir = get_ipython_dir()
147 self.default_config.Global.log_level = self.log_level
147 self.default_config.Global.log_level = self.log_level
148
148
149 def log_default_config(self):
149 def log_default_config(self):
150 self.log.debug('Default config loaded:')
150 self.log.debug('Default config loaded:')
151 self.log.debug(repr(self.default_config))
151 self.log.debug(repr(self.default_config))
152
152
153 def set_default_config_log_level(self):
153 def set_default_config_log_level(self):
154 try:
154 try:
155 self.log_level = self.default_config.Global.log_level
155 self.log_level = self.default_config.Global.log_level
156 except AttributeError:
156 except AttributeError:
157 # Fallback to the default_log_level class attribute
157 # Fallback to the default_log_level class attribute
158 pass
158 pass
159
159
160 def create_command_line_config(self):
160 def create_command_line_config(self):
161 """Create and return a command line config loader."""
161 """Create and return a command line config loader."""
162 return BaseAppArgParseConfigLoader(
162 return BaseAppArgParseConfigLoader(
163 description=self.description,
163 description=self.description,
164 version=release.version
164 version=release.version
165 )
165 )
166
166
167 def pre_load_command_line_config(self):
167 def pre_load_command_line_config(self):
168 """Do actions just before loading the command line config."""
168 """Do actions just before loading the command line config."""
169 pass
169 pass
170
170
171 def load_command_line_config(self):
171 def load_command_line_config(self):
172 """Load the command line config."""
172 """Load the command line config."""
173 loader = self.create_command_line_config()
173 loader = self.create_command_line_config()
174 self.command_line_config = loader.load_config()
174 self.command_line_config = loader.load_config()
175 self.extra_args = loader.get_extra_args()
175 self.extra_args = loader.get_extra_args()
176
176
177 def set_command_line_config_log_level(self):
177 def set_command_line_config_log_level(self):
178 try:
178 try:
179 self.log_level = self.command_line_config.Global.log_level
179 self.log_level = self.command_line_config.Global.log_level
180 except AttributeError:
180 except AttributeError:
181 pass
181 pass
182
182
183 def post_load_command_line_config(self):
183 def post_load_command_line_config(self):
184 """Do actions just after loading the command line config."""
184 """Do actions just after loading the command line config."""
185 pass
185 pass
186
186
187 def log_command_line_config(self):
187 def log_command_line_config(self):
188 self.log.debug("Command line config loaded:")
188 self.log.debug("Command line config loaded:")
189 self.log.debug(repr(self.command_line_config))
189 self.log.debug(repr(self.command_line_config))
190
190
191 def find_ipythondir(self):
191 def find_ipythondir(self):
192 """Set the IPython directory.
192 """Set the IPython directory.
193
193
194 This sets ``self.ipythondir``, but the actual value that is passed
194 This sets ``self.ipythondir``, but the actual value that is passed
195 to the application is kept in either ``self.default_config`` or
195 to the application is kept in either ``self.default_config`` or
196 ``self.command_line_config``. This also added ``self.ipythondir`` to
196 ``self.command_line_config``. This also added ``self.ipythondir`` to
197 ``sys.path`` so config files there can be references by other config
197 ``sys.path`` so config files there can be references by other config
198 files.
198 files.
199 """
199 """
200
200
201 try:
201 try:
202 self.ipythondir = self.command_line_config.Global.ipythondir
202 self.ipythondir = self.command_line_config.Global.ipythondir
203 except AttributeError:
203 except AttributeError:
204 self.ipythondir = self.default_config.Global.ipythondir
204 self.ipythondir = self.default_config.Global.ipythondir
205 sys.path.append(os.path.abspath(self.ipythondir))
205 sys.path.append(os.path.abspath(self.ipythondir))
206 if not os.path.isdir(self.ipythondir):
206 if not os.path.isdir(self.ipythondir):
207 os.makedirs(self.ipythondir, mode=0777)
207 os.makedirs(self.ipythondir, mode=0777)
208 self.log.debug("IPYTHONDIR set to: %s" % self.ipythondir)
208 self.log.debug("IPYTHONDIR set to: %s" % self.ipythondir)
209
209
210 def find_config_file_name(self):
210 def find_config_file_name(self):
211 """Find the config file name for this application.
211 """Find the config file name for this application.
212
212
213 This must set ``self.config_file_name`` to the filename of the
213 This must set ``self.config_file_name`` to the filename of the
214 config file to use (just the filename). The search paths for the
214 config file to use (just the filename). The search paths for the
215 config file are set in :meth:`find_config_file_paths` and then passed
215 config file are set in :meth:`find_config_file_paths` and then passed
216 to the config file loader where they are resolved to an absolute path.
216 to the config file loader where they are resolved to an absolute path.
217
217
218 If a profile has been set at the command line, this will resolve
218 If a profile has been set at the command line, this will resolve
219 it.
219 it.
220 """
220 """
221
221
222 try:
222 try:
223 self.config_file_name = self.command_line_config.Global.config_file
223 self.config_file_name = self.command_line_config.Global.config_file
224 except AttributeError:
224 except AttributeError:
225 pass
225 pass
226
226
227 try:
227 try:
228 self.profile_name = self.command_line_config.Global.profile
228 self.profile_name = self.command_line_config.Global.profile
229 name_parts = self.config_file_name.split('.')
229 name_parts = self.config_file_name.split('.')
230 name_parts.insert(1, '_' + self.profile_name + '.')
230 name_parts.insert(1, '_' + self.profile_name + '.')
231 self.config_file_name = ''.join(name_parts)
231 self.config_file_name = ''.join(name_parts)
232 except AttributeError:
232 except AttributeError:
233 pass
233 pass
234
234
235 def find_config_file_paths(self):
235 def find_config_file_paths(self):
236 """Set the search paths for resolving the config file.
236 """Set the search paths for resolving the config file.
237
237
238 This must set ``self.config_file_paths`` to a sequence of search
238 This must set ``self.config_file_paths`` to a sequence of search
239 paths to pass to the config file loader.
239 paths to pass to the config file loader.
240 """
240 """
241 self.config_file_paths = (os.getcwd(), self.ipythondir)
241 self.config_file_paths = (os.getcwd(), self.ipythondir)
242
242
243 def pre_load_file_config(self):
243 def pre_load_file_config(self):
244 """Do actions before the config file is loaded."""
244 """Do actions before the config file is loaded."""
245 pass
245 pass
246
246
247 def load_file_config(self):
247 def load_file_config(self):
248 """Load the config file.
248 """Load the config file.
249
249
250 This tries to load the config file from disk. If successful, the
250 This tries to load the config file from disk. If successful, the
251 ``CONFIG_FILE`` config variable is set to the resolved config file
251 ``CONFIG_FILE`` config variable is set to the resolved config file
252 location. If not successful, an empty config is used.
252 location. If not successful, an empty config is used.
253 """
253 """
254 self.log.debug("Attempting to load config file: <%s>" % self.config_file_name)
254 self.log.debug("Attempting to load config file: <%s>" % self.config_file_name)
255 loader = PyFileConfigLoader(self.config_file_name,
255 loader = PyFileConfigLoader(self.config_file_name,
256 path=self.config_file_paths)
256 path=self.config_file_paths)
257 try:
257 try:
258 self.file_config = loader.load_config()
258 self.file_config = loader.load_config()
259 self.file_config.Global.config_file = loader.full_filename
259 self.file_config.Global.config_file = loader.full_filename
260 except IOError:
260 except IOError:
261 # Only warn if the default config file was NOT being used.
261 # Only warn if the default config file was NOT being used.
262 if not self.config_file_name==self.default_config_file_name:
262 if not self.config_file_name==self.default_config_file_name:
263 self.log.warn("Config file not found, skipping: <%s>" % \
263 self.log.warn("Config file not found, skipping: <%s>" % \
264 self.config_file_name, exc_info=True)
264 self.config_file_name, exc_info=True)
265 self.file_config = Config()
265 self.file_config = Config()
266 except:
266 except:
267 self.log.warn("Error loading config file: <%s>" % \
267 self.log.warn("Error loading config file: <%s>" % \
268 self.config_file_name, exc_info=True)
268 self.config_file_name, exc_info=True)
269 self.file_config = Config()
269 self.file_config = Config()
270
270
271 def set_file_config_log_level(self):
271 def set_file_config_log_level(self):
272 # We need to keeep self.log_level updated. But we only use the value
272 # We need to keeep self.log_level updated. But we only use the value
273 # of the file_config if a value was not specified at the command
273 # of the file_config if a value was not specified at the command
274 # line, because the command line overrides everything.
274 # line, because the command line overrides everything.
275 if not hasattr(self.command_line_config.Global, 'log_level'):
275 if not hasattr(self.command_line_config.Global, 'log_level'):
276 try:
276 try:
277 self.log_level = self.file_config.Global.log_level
277 self.log_level = self.file_config.Global.log_level
278 except AttributeError:
278 except AttributeError:
279 pass # Use existing value
279 pass # Use existing value
280
280
281 def post_load_file_config(self):
281 def post_load_file_config(self):
282 """Do actions after the config file is loaded."""
282 """Do actions after the config file is loaded."""
283 pass
283 pass
284
284
285 def log_file_config(self):
285 def log_file_config(self):
286 if hasattr(self.file_config.Global, 'config_file'):
286 if hasattr(self.file_config.Global, 'config_file'):
287 self.log.debug("Config file loaded: <%s>" % self.file_config.Global.config_file)
287 self.log.debug("Config file loaded: <%s>" % self.file_config.Global.config_file)
288 self.log.debug(repr(self.file_config))
288 self.log.debug(repr(self.file_config))
289
289
290 def merge_configs(self):
290 def merge_configs(self):
291 """Merge the default, command line and file config objects."""
291 """Merge the default, command line and file config objects."""
292 config = Config()
292 config = Config()
293 config._merge(self.default_config)
293 config._merge(self.default_config)
294 config._merge(self.file_config)
294 config._merge(self.file_config)
295 config._merge(self.command_line_config)
295 config._merge(self.command_line_config)
296 self.master_config = config
296 self.master_config = config
297
297
298 def log_master_config(self):
298 def log_master_config(self):
299 self.log.debug("Master config created:")
299 self.log.debug("Master config created:")
300 self.log.debug(repr(self.master_config))
300 self.log.debug(repr(self.master_config))
301
301
302 def pre_construct(self):
302 def pre_construct(self):
303 """Do actions after the config has been built, but before construct."""
303 """Do actions after the config has been built, but before construct."""
304 pass
304 pass
305
305
306 def construct(self):
306 def construct(self):
307 """Construct the main components that make up this app."""
307 """Construct the main components that make up this app."""
308 self.log.debug("Constructing components for application")
308 self.log.debug("Constructing components for application")
309
309
310 def post_construct(self):
310 def post_construct(self):
311 """Do actions after construct, but before starting the app."""
311 """Do actions after construct, but before starting the app."""
312 pass
312 pass
313
313
314 def start_app(self):
314 def start_app(self):
315 """Actually start the app."""
315 """Actually start the app."""
316 self.log.debug("Starting application")
316 self.log.debug("Starting application")
317
317
318 #-------------------------------------------------------------------------
318 #-------------------------------------------------------------------------
319 # Utility methods
319 # Utility methods
320 #-------------------------------------------------------------------------
320 #-------------------------------------------------------------------------
321
321
322 def abort(self):
322 def abort(self):
323 """Abort the starting of the application."""
323 """Abort the starting of the application."""
324 self.log.critical("Aborting application: %s" % self.name, exc_info=True)
324 self.log.critical("Aborting application: %s" % self.name, exc_info=True)
325 sys.exit(1)
325 sys.exit(1)
326
326
327 def exit(self):
327 def exit(self):
328 self.log.critical("Aborting application: %s" % self.name)
328 self.log.critical("Aborting application: %s" % self.name)
329 sys.exit(1)
329 sys.exit(1)
330
330
331 def attempt(self, func, action='abort'):
331 def attempt(self, func, action='abort'):
332 try:
332 try:
333 func()
333 func()
334 except SystemExit:
334 except SystemExit:
335 self.exit()
335 self.exit()
336 except:
336 except:
337 if action == 'abort':
337 if action == 'abort':
338 self.abort()
338 self.abort()
339 elif action == 'exit':
339 elif action == 'exit':
340 self.exit()
340 self.exit()
341
341
342
342
343 class AppWithDirArgParseConfigLoader(ArgParseConfigLoader):
343 class AppWithDirArgParseConfigLoader(ArgParseConfigLoader):
344 """Default command line options for IPython based applications."""
344 """Default command line options for IPython based applications."""
345
345
346 def _add_other_arguments(self):
346 def _add_other_arguments(self):
347 self.parser.add_argument('-ipythondir', '--ipython-dir',
347 self.parser.add_argument('-ipythondir', '--ipython-dir',
348 dest='Global.ipythondir',type=str,
348 dest='Global.ipythondir',type=str,
349 help='Set to override default location of Global.ipythondir.',
349 help='Set to override default location of Global.ipythondir.',
350 default=NoConfigDefault,
350 default=NoConfigDefault,
351 metavar='Global.ipythondir')
351 metavar='Global.ipythondir')
352 self.parser.add_argument('-p','-profile', '--profile',
352 self.parser.add_argument('-p','-profile', '--profile',
353 dest='Global.profile',type=str,
353 dest='Global.profile',type=str,
354 help='The string name of the ipython profile to be used.',
354 help='The string name of the profile to be used. This determines '
355 'the name of the application dir: basename_<profile>. The basename is '
356 'determined by the particular application. The default profile '
357 'is named "default". This convention is used if the -app_dir '
358 'option is not used.',
355 default=NoConfigDefault,
359 default=NoConfigDefault,
356 metavar='Global.profile')
360 metavar='Global.profile')
357 self.parser.add_argument('-log_level', '--log-level',
361 self.parser.add_argument('-log_level', '--log-level',
358 dest="Global.log_level",type=int,
362 dest="Global.log_level",type=int,
359 help='Set the log level (0,10,20,30,40,50). Default is 30.',
363 help='Set the log level (0,10,20,30,40,50). Default is 30.',
360 default=NoConfigDefault)
364 default=NoConfigDefault)
361 self.parser.add_argument('-app_dir', '--app-dir',
365 self.parser.add_argument('-app_dir', '--app-dir',
362 dest='Global.app_dir',type=str,
366 dest='Global.app_dir',type=str,
363 help='Set the application directory where everything for this '
367 help='Set the application dir where everything for this '
364 'application will be found (including the config file).',
368 'application will be found (including the config file). This '
369 'overrides the logic used by the profile option.',
365 default=NoConfigDefault,
370 default=NoConfigDefault,
366 metavar='Global.app_dir')
371 metavar='Global.app_dir')
367
372
368
373
369 class ApplicationWithDir(Application):
374 class ApplicationWithDir(Application):
375 """An application that puts everything into a application directory.
370
376
371 name = 'appname'
377 Instead of looking for things in the ipythondir, this type of application
372 description = 'Application: foo and bar it.'
378 will use its own private directory called the "application directory"
373 config_file_name = 'appname_config.py'
379 for things like config files, log files, etc.
374 default_log_level = logging.WARN
380
381 The application directory is resolved as follows:
382
383 * If the ``--app-dir`` option is given, it is used.
384 * If ``--app-dir`` is not given, the application directory is resolve using
385 ``app_dir_basename`` and ``profile`` as ``<app_dir_basename>_<profile>``.
386 The search path for this directory is then i) cwd if it is found there
387 and ii) in ipythondir otherwise.
388
389 The config file for the application is to be put in the application
390 dir and named the value of the ``config_file_name`` class attribute.
391 """
392
393 # The basename used for the application dir: <app_dir_basename>_<profile>
394 app_dir_basename = 'cluster'
375
395
376 def create_default_config(self):
396 def create_default_config(self):
377 super(ApplicationWithDir, self).create_default_config()
397 super(ApplicationWithDir, self).create_default_config()
378 self.default_config.Global.profile = 'default'
398 self.default_config.Global.profile = 'default'
399 # The application dir. This is empty initially so the default is to
400 # try to resolve this using the profile.
379 self.default_config.Global.app_dir = ''
401 self.default_config.Global.app_dir = ''
380
402
381 def create_command_line_config(self):
403 def create_command_line_config(self):
382 """Create and return a command line config loader."""
404 """Create and return a command line config loader."""
383 return AppWithDirArgParseConfigLoader(
405 return AppWithDirArgParseConfigLoader(
384 description=self.description,
406 description=self.description,
385 version=release.version
407 version=release.version
386 )
408 )
387
409
388 def find_config_file_name(self):
410 def find_config_file_name(self):
389 """Find the config file name for this application."""
411 """Find the config file name for this application."""
390 self.find_app_dir()
412 self.find_app_dir()
391 self.create_app_dir()
413 self.create_app_dir()
392
414
393 def find_app_dir(self):
415 def find_app_dir(self):
394 """This resolves into full paths, the app directory.
416 """This resolves the app directory.
395
417
396 This method must set ``self.app_dir`` to the full path of
418 This method must set ``self.app_dir`` to the location of the app
397 the directory.
419 dir.
398 """
420 """
399 # Instead, first look for an explicit app_dir
421 # Instead, first look for an explicit app_dir
400 try:
422 try:
401 self.app_dir = self.command_line_config.Global.app_dir
423 self.app_dir = self.command_line_config.Global.app_dir
402 except AttributeError:
424 except AttributeError:
403 self.app_dir = self.default_config.Global.app_dir
425 self.app_dir = self.default_config.Global.app_dir
404 self.app_dir = os.path.expandvars(os.path.expanduser(self.app_dir))
426 self.app_dir = os.path.expandvars(os.path.expanduser(self.app_dir))
405 if not self.app_dir:
427 if not self.app_dir:
406 # Then look for a profile
428 # Then look for a profile
407 try:
429 try:
408 self.profile = self.command_line_config.Global.profile
430 self.profile = self.command_line_config.Global.profile
409 except AttributeError:
431 except AttributeError:
410 self.profile = self.default_config.Global.profile
432 self.profile = self.default_config.Global.profile
411 app_dir_name = 'cluster_' + self.profile
433 app_dir_name = self.app_dir_basename + '_' + self.profile
412 try_this = os.path.join(os.getcwd(), app_dir_name)
434 try_this = os.path.join(os.getcwd(), app_dir_name)
413 if os.path.isdir(try_this):
435 if os.path.isdir(try_this):
414 self.app_dir = try_this
436 self.app_dir = try_this
415 else:
437 else:
416 self.app_dir = os.path.join(self.ipythondir, app_dir_name)
438 self.app_dir = os.path.join(self.ipythondir, app_dir_name)
417 # These have to be set because they could be different from the one
439 # These have to be set because they could be different from the one
418 # that we just computed. Because command line has the highest
440 # that we just computed. Because command line has the highest
419 # priority, this will always end up in the master_config.
441 # priority, this will always end up in the master_config.
420 self.default_config.Global.app_dir = self.app_dir
442 self.default_config.Global.app_dir = self.app_dir
421 self.command_line_config.Global.app_dir = self.app_dir
443 self.command_line_config.Global.app_dir = self.app_dir
422
444
423 def create_app_dir(self):
445 def create_app_dir(self):
424 """Make sure that the cluster, security and log dirs exist."""
446 """Make sure that the app dir exists."""
425 if not os.path.isdir(self.app_dir):
447 if not os.path.isdir(self.app_dir):
426 os.makedirs(self.app_dir, mode=0777)
448 os.makedirs(self.app_dir, mode=0777)
427
449
428 def find_config_file_paths(self):
450 def find_config_file_paths(self):
429 """Set the search paths for resolving the config file."""
451 """Set the search paths for resolving the config file."""
430 self.config_file_paths = (self.app_dir,)
452 self.config_file_paths = (self.app_dir,)
@@ -1,325 +1,337 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 A lightweight component system for IPython.
4 A lightweight component system for IPython.
5
5
6 Authors:
6 Authors:
7
7
8 * Brian Granger
8 * Brian Granger
9 * Fernando Perez
9 * Fernando Perez
10 """
10 """
11
11
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13 # Copyright (C) 2008-2009 The IPython Development Team
13 # Copyright (C) 2008-2009 The IPython Development Team
14 #
14 #
15 # Distributed under the terms of the BSD License. The full license is in
15 # Distributed under the terms of the BSD License. The full license is in
16 # the file COPYING, distributed as part of this software.
16 # the file COPYING, distributed as part of this software.
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 #-----------------------------------------------------------------------------
19 #-----------------------------------------------------------------------------
20 # Imports
20 # Imports
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22
22
23 from copy import deepcopy
23 from copy import deepcopy
24 import datetime
24 import datetime
25 from weakref import WeakValueDictionary
25 from weakref import WeakValueDictionary
26
26
27 from IPython.utils.importstring import import_item
27 from IPython.utils.importstring import import_item
28 from IPython.config.loader import Config
28 from IPython.config.loader import Config
29 from IPython.utils.traitlets import (
29 from IPython.utils.traitlets import (
30 HasTraitlets, TraitletError, MetaHasTraitlets, Instance, This
30 HasTraitlets, TraitletError, MetaHasTraitlets, Instance, This
31 )
31 )
32
32
33
33
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35 # Helper classes for Components
35 # Helper classes for Components
36 #-----------------------------------------------------------------------------
36 #-----------------------------------------------------------------------------
37
37
38
38
39 class ComponentError(Exception):
39 class ComponentError(Exception):
40 pass
40 pass
41
41
42 class MetaComponentTracker(type):
42 class MetaComponentTracker(type):
43 """A metaclass that tracks instances of Components and its subclasses."""
43 """A metaclass that tracks instances of Components and its subclasses."""
44
44
45 def __init__(cls, name, bases, d):
45 def __init__(cls, name, bases, d):
46 super(MetaComponentTracker, cls).__init__(name, bases, d)
46 super(MetaComponentTracker, cls).__init__(name, bases, d)
47 cls.__instance_refs = WeakValueDictionary()
47 cls.__instance_refs = WeakValueDictionary()
48 cls.__numcreated = 0
48 cls.__numcreated = 0
49
49
50 def __call__(cls, *args, **kw):
50 def __call__(cls, *args, **kw):
51 """Called when a class is called (instantiated)!!!
51 """Called when a class is called (instantiated)!!!
52
52
53 When a Component or subclass is instantiated, this is called and
53 When a Component or subclass is instantiated, this is called and
54 the instance is saved in a WeakValueDictionary for tracking.
54 the instance is saved in a WeakValueDictionary for tracking.
55 """
55 """
56 instance = cls.__new__(cls, *args, **kw)
56 instance = cls.__new__(cls, *args, **kw)
57
57
58 # Register the instance before __init__ is called so get_instances
58 # Register the instance before __init__ is called so get_instances
59 # works inside __init__ methods!
59 # works inside __init__ methods!
60 indices = cls.register_instance(instance)
60 indices = cls.register_instance(instance)
61
61
62 # This is in a try/except because of the __init__ method fails, the
62 # This is in a try/except because of the __init__ method fails, the
63 # instance is discarded and shouldn't be tracked.
63 # instance is discarded and shouldn't be tracked.
64 try:
64 try:
65 if isinstance(instance, cls):
65 if isinstance(instance, cls):
66 cls.__init__(instance, *args, **kw)
66 cls.__init__(instance, *args, **kw)
67 except:
67 except:
68 # Unregister the instance because __init__ failed!
68 # Unregister the instance because __init__ failed!
69 cls.unregister_instances(indices)
69 cls.unregister_instances(indices)
70 raise
70 raise
71 else:
71 else:
72 return instance
72 return instance
73
73
74 def register_instance(cls, instance):
74 def register_instance(cls, instance):
75 """Register instance with cls and its subclasses."""
75 """Register instance with cls and its subclasses."""
76 # indices is a list of the keys used to register the instance
76 # indices is a list of the keys used to register the instance
77 # with. This list is needed if the instance needs to be unregistered.
77 # with. This list is needed if the instance needs to be unregistered.
78 indices = []
78 indices = []
79 for c in cls.__mro__:
79 for c in cls.__mro__:
80 if issubclass(cls, c) and issubclass(c, Component):
80 if issubclass(cls, c) and issubclass(c, Component):
81 c.__numcreated += 1
81 c.__numcreated += 1
82 indices.append(c.__numcreated)
82 indices.append(c.__numcreated)
83 c.__instance_refs[c.__numcreated] = instance
83 c.__instance_refs[c.__numcreated] = instance
84 else:
84 else:
85 break
85 break
86 return indices
86 return indices
87
87
88 def unregister_instances(cls, indices):
88 def unregister_instances(cls, indices):
89 """Unregister instance with cls and its subclasses."""
89 """Unregister instance with cls and its subclasses."""
90 for c, index in zip(cls.__mro__, indices):
90 for c, index in zip(cls.__mro__, indices):
91 try:
91 try:
92 del c.__instance_refs[index]
92 del c.__instance_refs[index]
93 except KeyError:
93 except KeyError:
94 pass
94 pass
95
95
96 def clear_instances(cls):
96 def clear_instances(cls):
97 """Clear all instances tracked by cls."""
97 """Clear all instances tracked by cls."""
98 cls.__instance_refs.clear()
98 cls.__instance_refs.clear()
99 cls.__numcreated = 0
99 cls.__numcreated = 0
100
100
101 def get_instances(cls, name=None, root=None, klass=None):
101 def get_instances(cls, name=None, root=None, klass=None):
102 """Get all instances of cls and its subclasses.
102 """Get all instances of cls and its subclasses.
103
103
104 Parameters
104 Parameters
105 ----------
105 ----------
106 name : str
106 name : str
107 Limit to components with this name.
107 Limit to components with this name.
108 root : Component or subclass
108 root : Component or subclass
109 Limit to components having this root.
109 Limit to components having this root.
110 klass : class or str
110 klass : class or str
111 Limits to instances of the class or its subclasses. If a str
111 Limits to instances of the class or its subclasses. If a str
112 is given ut must be in the form 'foo.bar.MyClass'. The str
112 is given ut must be in the form 'foo.bar.MyClass'. The str
113 form of this argument is useful for forward declarations.
113 form of this argument is useful for forward declarations.
114 """
114 """
115 if klass is not None:
115 if klass is not None:
116 if isinstance(klass, basestring):
116 if isinstance(klass, basestring):
117 klass = import_item(klass)
117 klass = import_item(klass)
118 # Limit search to instances of klass for performance
118 # Limit search to instances of klass for performance
119 if issubclass(klass, Component):
119 if issubclass(klass, Component):
120 return klass.get_instances(name=name, root=root)
120 return klass.get_instances(name=name, root=root)
121 instances = cls.__instance_refs.values()
121 instances = cls.__instance_refs.values()
122 if name is not None:
122 if name is not None:
123 instances = [i for i in instances if i.name == name]
123 instances = [i for i in instances if i.name == name]
124 if klass is not None:
124 if klass is not None:
125 instances = [i for i in instances if isinstance(i, klass)]
125 instances = [i for i in instances if isinstance(i, klass)]
126 if root is not None:
126 if root is not None:
127 instances = [i for i in instances if i.root == root]
127 instances = [i for i in instances if i.root == root]
128 return instances
128 return instances
129
129
130 def get_instances_by_condition(cls, call, name=None, root=None,
130 def get_instances_by_condition(cls, call, name=None, root=None,
131 klass=None):
131 klass=None):
132 """Get all instances of cls, i such that call(i)==True.
132 """Get all instances of cls, i such that call(i)==True.
133
133
134 This also takes the ``name`` and ``root`` and ``classname``
134 This also takes the ``name`` and ``root`` and ``classname``
135 arguments of :meth:`get_instance`
135 arguments of :meth:`get_instance`
136 """
136 """
137 return [i for i in cls.get_instances(name, root, klass) if call(i)]
137 return [i for i in cls.get_instances(name, root, klass) if call(i)]
138
138
139
139
140 def masquerade_as(instance, cls):
140 def masquerade_as(instance, cls):
141 """Let instance masquerade as an instance of cls.
141 """Let instance masquerade as an instance of cls.
142
142
143 Sometimes, such as in testing code, it is useful to let a class
143 Sometimes, such as in testing code, it is useful to let a class
144 masquerade as another. Python, being duck typed, allows this by
144 masquerade as another. Python, being duck typed, allows this by
145 default. But, instances of components are tracked by their class type.
145 default. But, instances of components are tracked by their class type.
146
146
147 After calling this, ``cls.get_instances()`` will return ``instance``. This
147 After calling this, ``cls.get_instances()`` will return ``instance``. This
148 does not, however, cause ``isinstance(instance, cls)`` to return ``True``.
148 does not, however, cause ``isinstance(instance, cls)`` to return ``True``.
149
149
150 Parameters
150 Parameters
151 ----------
151 ----------
152 instance : an instance of a Component or Component subclass
152 instance : an instance of a Component or Component subclass
153 The instance that will pretend to be a cls.
153 The instance that will pretend to be a cls.
154 cls : subclass of Component
154 cls : subclass of Component
155 The Component subclass that instance will pretend to be.
155 The Component subclass that instance will pretend to be.
156 """
156 """
157 cls.register_instance(instance)
157 cls.register_instance(instance)
158
158
159
159
160 class ComponentNameGenerator(object):
160 class ComponentNameGenerator(object):
161 """A Singleton to generate unique component names."""
161 """A Singleton to generate unique component names."""
162
162
163 def __init__(self, prefix):
163 def __init__(self, prefix):
164 self.prefix = prefix
164 self.prefix = prefix
165 self.i = 0
165 self.i = 0
166
166
167 def __call__(self):
167 def __call__(self):
168 count = self.i
168 count = self.i
169 self.i += 1
169 self.i += 1
170 return "%s%s" % (self.prefix, count)
170 return "%s%s" % (self.prefix, count)
171
171
172
172
173 ComponentNameGenerator = ComponentNameGenerator('ipython.component')
173 ComponentNameGenerator = ComponentNameGenerator('ipython.component')
174
174
175
175
176 class MetaComponent(MetaHasTraitlets, MetaComponentTracker):
176 class MetaComponent(MetaHasTraitlets, MetaComponentTracker):
177 pass
177 pass
178
178
179
179
180 #-----------------------------------------------------------------------------
180 #-----------------------------------------------------------------------------
181 # Component implementation
181 # Component implementation
182 #-----------------------------------------------------------------------------
182 #-----------------------------------------------------------------------------
183
183
184
184
185 class Component(HasTraitlets):
185 class Component(HasTraitlets):
186
186
187 __metaclass__ = MetaComponent
187 __metaclass__ = MetaComponent
188
188
189 # Traitlets are fun!
189 # Traitlets are fun!
190 config = Instance(Config,(),{})
190 config = Instance(Config,(),{})
191 parent = This()
191 parent = This()
192 root = This()
192 root = This()
193 created = None
193 created = None
194
194
195 def __init__(self, parent, name=None, config=None):
195 def __init__(self, parent, name=None, config=None):
196 """Create a component given a parent and possibly and name and config.
196 """Create a component given a parent and possibly and name and config.
197
197
198 Parameters
198 Parameters
199 ----------
199 ----------
200 parent : Component subclass
200 parent : Component subclass
201 The parent in the component graph. The parent is used
201 The parent in the component graph. The parent is used
202 to get the root of the component graph.
202 to get the root of the component graph.
203 name : str
203 name : str
204 The unique name of the component. If empty, then a unique
204 The unique name of the component. If empty, then a unique
205 one will be autogenerated.
205 one will be autogenerated.
206 config : Config
206 config : Config
207 If this is empty, self.config = parent.config, otherwise
207 If this is empty, self.config = parent.config, otherwise
208 self.config = config and root.config is ignored. This argument
208 self.config = config and root.config is ignored. This argument
209 should only be used to *override* the automatic inheritance of
209 should only be used to *override* the automatic inheritance of
210 parent.config. If a caller wants to modify parent.config
210 parent.config. If a caller wants to modify parent.config
211 (not override), the caller should make a copy and change
211 (not override), the caller should make a copy and change
212 attributes and then pass the copy to this argument.
212 attributes and then pass the copy to this argument.
213
213
214 Notes
214 Notes
215 -----
215 -----
216 Subclasses of Component must call the :meth:`__init__` method of
216 Subclasses of Component must call the :meth:`__init__` method of
217 :class:`Component` *before* doing anything else and using
217 :class:`Component` *before* doing anything else and using
218 :func:`super`::
218 :func:`super`::
219
219
220 class MyComponent(Component):
220 class MyComponent(Component):
221 def __init__(self, parent, name=None, config=None):
221 def __init__(self, parent, name=None, config=None):
222 super(MyComponent, self).__init__(parent, name, config)
222 super(MyComponent, self).__init__(parent, name, config)
223 # Then any other code you need to finish initialization.
223 # Then any other code you need to finish initialization.
224
224
225 This ensures that the :attr:`parent`, :attr:`name` and :attr:`config`
225 This ensures that the :attr:`parent`, :attr:`name` and :attr:`config`
226 attributes are handled properly.
226 attributes are handled properly.
227 """
227 """
228 super(Component, self).__init__()
228 super(Component, self).__init__()
229 self._children = []
229 self._children = []
230 if name is None:
230 if name is None:
231 self.name = ComponentNameGenerator()
231 self.name = ComponentNameGenerator()
232 else:
232 else:
233 self.name = name
233 self.name = name
234 self.root = self # This is the default, it is set when parent is set
234 self.root = self # This is the default, it is set when parent is set
235 self.parent = parent
235 self.parent = parent
236 if config is not None:
236 if config is not None:
237 self.config = config
237 self.config = config
238 # We used to deepcopy, but for now we are trying to just save
238 # We used to deepcopy, but for now we are trying to just save
239 # by reference. This *could* have side effects as all components
239 # by reference. This *could* have side effects as all components
240 # will share config.
240 # will share config.
241 # self.config = deepcopy(config)
241 # self.config = deepcopy(config)
242 else:
242 else:
243 if self.parent is not None:
243 if self.parent is not None:
244 self.config = self.parent.config
244 self.config = self.parent.config
245 # We used to deepcopy, but for now we are trying to just save
245 # We used to deepcopy, but for now we are trying to just save
246 # by reference. This *could* have side effects as all components
246 # by reference. This *could* have side effects as all components
247 # will share config.
247 # will share config.
248 # self.config = deepcopy(self.parent.config)
248 # self.config = deepcopy(self.parent.config)
249
249
250 self.created = datetime.datetime.now()
250 self.created = datetime.datetime.now()
251
251
252 #-------------------------------------------------------------------------
252 #-------------------------------------------------------------------------
253 # Static traitlet notifiations
253 # Static traitlet notifiations
254 #-------------------------------------------------------------------------
254 #-------------------------------------------------------------------------
255
255
256 def _parent_changed(self, name, old, new):
256 def _parent_changed(self, name, old, new):
257 if old is not None:
257 if old is not None:
258 old._remove_child(self)
258 old._remove_child(self)
259 if new is not None:
259 if new is not None:
260 new._add_child(self)
260 new._add_child(self)
261
261
262 if new is None:
262 if new is None:
263 self.root = self
263 self.root = self
264 else:
264 else:
265 self.root = new.root
265 self.root = new.root
266
266
267 def _root_changed(self, name, old, new):
267 def _root_changed(self, name, old, new):
268 if self.parent is None:
268 if self.parent is None:
269 if not (new is self):
269 if not (new is self):
270 raise ComponentError("Root not self, but parent is None.")
270 raise ComponentError("Root not self, but parent is None.")
271 else:
271 else:
272 if not self.parent.root is new:
272 if not self.parent.root is new:
273 raise ComponentError("Error in setting the root attribute: "
273 raise ComponentError("Error in setting the root attribute: "
274 "root != parent.root")
274 "root != parent.root")
275
275
276 def _config_changed(self, name, old, new):
276 def _config_changed(self, name, old, new):
277 """Update all the class traits having ``config=True`` as metadata.
277 """Update all the class traits having ``config=True`` as metadata.
278
278
279 For any class traitlet with a ``config`` metadata attribute that is
279 For any class traitlet with a ``config`` metadata attribute that is
280 ``True``, we update the traitlet with the value of the corresponding
280 ``True``, we update the traitlet with the value of the corresponding
281 config entry.
281 config entry.
282 """
282 """
283 # Get all traitlets with a config metadata entry that is True
283 # Get all traitlets with a config metadata entry that is True
284 traitlets = self.traitlets(config=True)
284 traitlets = self.traitlets(config=True)
285
285
286 # We auto-load config section for this class as well as any parent
286 # We auto-load config section for this class as well as any parent
287 # classes that are Component subclasses. This starts with Component
287 # classes that are Component subclasses. This starts with Component
288 # and works down the mro loading the config for each section.
288 # and works down the mro loading the config for each section.
289 section_names = [cls.__name__ for cls in \
289 section_names = [cls.__name__ for cls in \
290 reversed(self.__class__.__mro__) if
290 reversed(self.__class__.__mro__) if
291 issubclass(cls, Component) and issubclass(self.__class__, cls)]
291 issubclass(cls, Component) and issubclass(self.__class__, cls)]
292
292
293 for sname in section_names:
293 for sname in section_names:
294 # Don't do a blind getattr as that would cause the config to
294 # Don't do a blind getattr as that would cause the config to
295 # dynamically create the section with name self.__class__.__name__.
295 # dynamically create the section with name self.__class__.__name__.
296 if new._has_section(sname):
296 if new._has_section(sname):
297 my_config = new[sname]
297 my_config = new[sname]
298 for k, v in traitlets.items():
298 for k, v in traitlets.items():
299 # Don't allow traitlets with config=True to start with
300 # uppercase. Otherwise, they are confused with Config
301 # subsections. But, developers shouldn't have uppercase
302 # attributes anyways! (PEP 6)
303 if k[0].upper()==k[0] and not k.startswith('_'):
304 raise ComponentError('Component traitlets with '
305 'config=True must start with a lowercase so they are '
306 'not confused with Config subsections: %s.%s' % \
307 (self.__class__.__name__, k))
299 try:
308 try:
309 # Here we grab the value from the config
310 # If k has the naming convention of a config
311 # section, it will be auto created.
300 config_value = my_config[k]
312 config_value = my_config[k]
301 except KeyError:
313 except KeyError:
302 pass
314 pass
303 else:
315 else:
304 # print "Setting %s.%s from %s.%s=%r" % \
316 # print "Setting %s.%s from %s.%s=%r" % \
305 # (self.__class__.__name__,k,sname,k,config_value)
317 # (self.__class__.__name__,k,sname,k,config_value)
306 setattr(self, k, config_value)
318 setattr(self, k, config_value)
307
319
308 @property
320 @property
309 def children(self):
321 def children(self):
310 """A list of all my child components."""
322 """A list of all my child components."""
311 return self._children
323 return self._children
312
324
313 def _remove_child(self, child):
325 def _remove_child(self, child):
314 """A private method for removing children components."""
326 """A private method for removing children components."""
315 if child in self._children:
327 if child in self._children:
316 index = self._children.index(child)
328 index = self._children.index(child)
317 del self._children[index]
329 del self._children[index]
318
330
319 def _add_child(self, child):
331 def _add_child(self, child):
320 """A private method for adding children components."""
332 """A private method for adding children components."""
321 if child not in self._children:
333 if child not in self._children:
322 self._children.append(child)
334 self._children.append(child)
323
335
324 def __repr__(self):
336 def __repr__(self):
325 return "<%s('%s')>" % (self.__class__.__name__, self.name)
337 return "<%s('%s')>" % (self.__class__.__name__, self.name)
@@ -1,545 +1,545 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 The main IPython application object
4 The main IPython application object
5
5
6 Authors:
6 Authors:
7
7
8 * Brian Granger
8 * Brian Granger
9 * Fernando Perez
9 * Fernando Perez
10
10
11 Notes
11 Notes
12 -----
12 -----
13 """
13 """
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Copyright (C) 2008-2009 The IPython Development Team
16 # Copyright (C) 2008-2009 The IPython Development Team
17 #
17 #
18 # Distributed under the terms of the BSD License. The full license is in
18 # Distributed under the terms of the BSD License. The full license is in
19 # the file COPYING, distributed as part of this software.
19 # the file COPYING, distributed as part of this software.
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Imports
23 # Imports
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 import logging
26 import logging
27 import os
27 import os
28 import sys
28 import sys
29 import warnings
29 import warnings
30
30
31 from IPython.core.application import Application, BaseAppArgParseConfigLoader
31 from IPython.core.application import Application, BaseAppArgParseConfigLoader
32 from IPython.core import release
32 from IPython.core import release
33 from IPython.core.iplib import InteractiveShell
33 from IPython.core.iplib import InteractiveShell
34 from IPython.config.loader import (
34 from IPython.config.loader import (
35 NoConfigDefault,
35 NoConfigDefault,
36 Config,
36 Config,
37 ConfigError,
37 ConfigError,
38 PyFileConfigLoader
38 PyFileConfigLoader
39 )
39 )
40
40
41 from IPython.lib import inputhook
41 from IPython.lib import inputhook
42
42
43 from IPython.utils.ipstruct import Struct
43 from IPython.utils.ipstruct import Struct
44 from IPython.utils.genutils import filefind, get_ipython_dir
44 from IPython.utils.genutils import filefind, get_ipython_dir
45
45
46 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
47 # Utilities and helpers
47 # Utilities and helpers
48 #-----------------------------------------------------------------------------
48 #-----------------------------------------------------------------------------
49
49
50
50
51 ipython_desc = """
51 ipython_desc = """
52 A Python shell with automatic history (input and output), dynamic object
52 A Python shell with automatic history (input and output), dynamic object
53 introspection, easier configuration, command completion, access to the system
53 introspection, easier configuration, command completion, access to the system
54 shell and more.
54 shell and more.
55 """
55 """
56
56
57 def pylab_warning():
57 def pylab_warning():
58 msg = """
58 msg = """
59
59
60 IPython's -pylab mode has been disabled until matplotlib supports this version
60 IPython's -pylab mode has been disabled until matplotlib supports this version
61 of IPython. This version of IPython has greatly improved GUI integration that
61 of IPython. This version of IPython has greatly improved GUI integration that
62 matplotlib will soon be able to take advantage of. This will eventually
62 matplotlib will soon be able to take advantage of. This will eventually
63 result in greater stability and a richer API for matplotlib under IPython.
63 result in greater stability and a richer API for matplotlib under IPython.
64 However during this transition, you will either need to use an older version
64 However during this transition, you will either need to use an older version
65 of IPython, or do the following to use matplotlib interactively::
65 of IPython, or do the following to use matplotlib interactively::
66
66
67 import matplotlib
67 import matplotlib
68 matplotlib.interactive(True)
68 matplotlib.interactive(True)
69 matplotlib.use('wxagg') # adjust for your backend
69 matplotlib.use('wxagg') # adjust for your backend
70 %gui -a wx # adjust for your GUI
70 %gui -a wx # adjust for your GUI
71 from matplotlib import pyplot as plt
71 from matplotlib import pyplot as plt
72
72
73 See the %gui magic for information on the new interface.
73 See the %gui magic for information on the new interface.
74 """
74 """
75 warnings.warn(msg, category=DeprecationWarning, stacklevel=1)
75 warnings.warn(msg, category=DeprecationWarning, stacklevel=1)
76
76
77
77
78 #-----------------------------------------------------------------------------
78 #-----------------------------------------------------------------------------
79 # Main classes and functions
79 # Main classes and functions
80 #-----------------------------------------------------------------------------
80 #-----------------------------------------------------------------------------
81
81
82 cl_args = (
82 cl_args = (
83 (('-autocall',), dict(
83 (('-autocall',), dict(
84 type=int, dest='InteractiveShell.autocall', default=NoConfigDefault,
84 type=int, dest='InteractiveShell.autocall', default=NoConfigDefault,
85 help='Set the autocall value (0,1,2).',
85 help='Set the autocall value (0,1,2).',
86 metavar='InteractiveShell.autocall')
86 metavar='InteractiveShell.autocall')
87 ),
87 ),
88 (('-autoindent',), dict(
88 (('-autoindent',), dict(
89 action='store_true', dest='InteractiveShell.autoindent', default=NoConfigDefault,
89 action='store_true', dest='InteractiveShell.autoindent', default=NoConfigDefault,
90 help='Turn on autoindenting.')
90 help='Turn on autoindenting.')
91 ),
91 ),
92 (('-noautoindent',), dict(
92 (('-noautoindent',), dict(
93 action='store_false', dest='InteractiveShell.autoindent', default=NoConfigDefault,
93 action='store_false', dest='InteractiveShell.autoindent', default=NoConfigDefault,
94 help='Turn off autoindenting.')
94 help='Turn off autoindenting.')
95 ),
95 ),
96 (('-automagic',), dict(
96 (('-automagic',), dict(
97 action='store_true', dest='InteractiveShell.automagic', default=NoConfigDefault,
97 action='store_true', dest='InteractiveShell.automagic', default=NoConfigDefault,
98 help='Turn on the auto calling of magic commands.')
98 help='Turn on the auto calling of magic commands.')
99 ),
99 ),
100 (('-noautomagic',), dict(
100 (('-noautomagic',), dict(
101 action='store_false', dest='InteractiveShell.automagic', default=NoConfigDefault,
101 action='store_false', dest='InteractiveShell.automagic', default=NoConfigDefault,
102 help='Turn off the auto calling of magic commands.')
102 help='Turn off the auto calling of magic commands.')
103 ),
103 ),
104 (('-autoedit_syntax',), dict(
104 (('-autoedit_syntax',), dict(
105 action='store_true', dest='InteractiveShell.autoedit_syntax', default=NoConfigDefault,
105 action='store_true', dest='InteractiveShell.autoedit_syntax', default=NoConfigDefault,
106 help='Turn on auto editing of files with syntax errors.')
106 help='Turn on auto editing of files with syntax errors.')
107 ),
107 ),
108 (('-noautoedit_syntax',), dict(
108 (('-noautoedit_syntax',), dict(
109 action='store_false', dest='InteractiveShell.autoedit_syntax', default=NoConfigDefault,
109 action='store_false', dest='InteractiveShell.autoedit_syntax', default=NoConfigDefault,
110 help='Turn off auto editing of files with syntax errors.')
110 help='Turn off auto editing of files with syntax errors.')
111 ),
111 ),
112 (('-banner',), dict(
112 (('-banner',), dict(
113 action='store_true', dest='Global.display_banner', default=NoConfigDefault,
113 action='store_true', dest='Global.display_banner', default=NoConfigDefault,
114 help='Display a banner upon starting IPython.')
114 help='Display a banner upon starting IPython.')
115 ),
115 ),
116 (('-nobanner',), dict(
116 (('-nobanner',), dict(
117 action='store_false', dest='Global.display_banner', default=NoConfigDefault,
117 action='store_false', dest='Global.display_banner', default=NoConfigDefault,
118 help="Don't display a banner upon starting IPython.")
118 help="Don't display a banner upon starting IPython.")
119 ),
119 ),
120 (('-cache_size',), dict(
120 (('-cache_size',), dict(
121 type=int, dest='InteractiveShell.cache_size', default=NoConfigDefault,
121 type=int, dest='InteractiveShell.cache_size', default=NoConfigDefault,
122 help="Set the size of the output cache.",
122 help="Set the size of the output cache.",
123 metavar='InteractiveShell.cache_size')
123 metavar='InteractiveShell.cache_size')
124 ),
124 ),
125 (('-classic',), dict(
125 (('-classic',), dict(
126 action='store_true', dest='Global.classic', default=NoConfigDefault,
126 action='store_true', dest='Global.classic', default=NoConfigDefault,
127 help="Gives IPython a similar feel to the classic Python prompt.")
127 help="Gives IPython a similar feel to the classic Python prompt.")
128 ),
128 ),
129 (('-colors',), dict(
129 (('-colors',), dict(
130 type=str, dest='InteractiveShell.colors', default=NoConfigDefault,
130 type=str, dest='InteractiveShell.colors', default=NoConfigDefault,
131 help="Set the color scheme (NoColor, Linux, and LightBG).",
131 help="Set the color scheme (NoColor, Linux, and LightBG).",
132 metavar='InteractiveShell.colors')
132 metavar='InteractiveShell.colors')
133 ),
133 ),
134 (('-color_info',), dict(
134 (('-color_info',), dict(
135 action='store_true', dest='InteractiveShell.color_info', default=NoConfigDefault,
135 action='store_true', dest='InteractiveShell.color_info', default=NoConfigDefault,
136 help="Enable using colors for info related things.")
136 help="Enable using colors for info related things.")
137 ),
137 ),
138 (('-nocolor_info',), dict(
138 (('-nocolor_info',), dict(
139 action='store_false', dest='InteractiveShell.color_info', default=NoConfigDefault,
139 action='store_false', dest='InteractiveShell.color_info', default=NoConfigDefault,
140 help="Disable using colors for info related things.")
140 help="Disable using colors for info related things.")
141 ),
141 ),
142 (('-confirm_exit',), dict(
142 (('-confirm_exit',), dict(
143 action='store_true', dest='InteractiveShell.confirm_exit', default=NoConfigDefault,
143 action='store_true', dest='InteractiveShell.confirm_exit', default=NoConfigDefault,
144 help="Prompt the user when existing.")
144 help="Prompt the user when existing.")
145 ),
145 ),
146 (('-noconfirm_exit',), dict(
146 (('-noconfirm_exit',), dict(
147 action='store_false', dest='InteractiveShell.confirm_exit', default=NoConfigDefault,
147 action='store_false', dest='InteractiveShell.confirm_exit', default=NoConfigDefault,
148 help="Don't prompt the user when existing.")
148 help="Don't prompt the user when existing.")
149 ),
149 ),
150 (('-deep_reload',), dict(
150 (('-deep_reload',), dict(
151 action='store_true', dest='InteractiveShell.deep_reload', default=NoConfigDefault,
151 action='store_true', dest='InteractiveShell.deep_reload', default=NoConfigDefault,
152 help="Enable deep (recursive) reloading by default.")
152 help="Enable deep (recursive) reloading by default.")
153 ),
153 ),
154 (('-nodeep_reload',), dict(
154 (('-nodeep_reload',), dict(
155 action='store_false', dest='InteractiveShell.deep_reload', default=NoConfigDefault,
155 action='store_false', dest='InteractiveShell.deep_reload', default=NoConfigDefault,
156 help="Disable deep (recursive) reloading by default.")
156 help="Disable deep (recursive) reloading by default.")
157 ),
157 ),
158 (('-editor',), dict(
158 (('-editor',), dict(
159 type=str, dest='InteractiveShell.editor', default=NoConfigDefault,
159 type=str, dest='InteractiveShell.editor', default=NoConfigDefault,
160 help="Set the editor used by IPython (default to $EDITOR/vi/notepad).",
160 help="Set the editor used by IPython (default to $EDITOR/vi/notepad).",
161 metavar='InteractiveShell.editor')
161 metavar='InteractiveShell.editor')
162 ),
162 ),
163 (('-log','-l'), dict(
163 (('-log','-l'), dict(
164 action='store_true', dest='InteractiveShell.logstart', default=NoConfigDefault,
164 action='store_true', dest='InteractiveShell.logstart', default=NoConfigDefault,
165 help="Start logging to the default file (./ipython_log.py).")
165 help="Start logging to the default file (./ipython_log.py).")
166 ),
166 ),
167 (('-logfile','-lf'), dict(
167 (('-logfile','-lf'), dict(
168 type=str, dest='InteractiveShell.logfile', default=NoConfigDefault,
168 type=str, dest='InteractiveShell.logfile', default=NoConfigDefault,
169 help="Start logging to logfile.",
169 help="Start logging to logfile.",
170 metavar='InteractiveShell.logfile')
170 metavar='InteractiveShell.logfile')
171 ),
171 ),
172 (('-logappend','-la'), dict(
172 (('-logappend','-la'), dict(
173 type=str, dest='InteractiveShell.logappend', default=NoConfigDefault,
173 type=str, dest='InteractiveShell.logappend', default=NoConfigDefault,
174 help="Start logging to logappend in append mode.",
174 help="Start logging to logappend in append mode.",
175 metavar='InteractiveShell.logfile')
175 metavar='InteractiveShell.logfile')
176 ),
176 ),
177 (('-pdb',), dict(
177 (('-pdb',), dict(
178 action='store_true', dest='InteractiveShell.pdb', default=NoConfigDefault,
178 action='store_true', dest='InteractiveShell.pdb', default=NoConfigDefault,
179 help="Enable auto calling the pdb debugger after every exception.")
179 help="Enable auto calling the pdb debugger after every exception.")
180 ),
180 ),
181 (('-nopdb',), dict(
181 (('-nopdb',), dict(
182 action='store_false', dest='InteractiveShell.pdb', default=NoConfigDefault,
182 action='store_false', dest='InteractiveShell.pdb', default=NoConfigDefault,
183 help="Disable auto calling the pdb debugger after every exception.")
183 help="Disable auto calling the pdb debugger after every exception.")
184 ),
184 ),
185 (('-pprint',), dict(
185 (('-pprint',), dict(
186 action='store_true', dest='InteractiveShell.pprint', default=NoConfigDefault,
186 action='store_true', dest='InteractiveShell.pprint', default=NoConfigDefault,
187 help="Enable auto pretty printing of results.")
187 help="Enable auto pretty printing of results.")
188 ),
188 ),
189 (('-nopprint',), dict(
189 (('-nopprint',), dict(
190 action='store_false', dest='InteractiveShell.pprint', default=NoConfigDefault,
190 action='store_false', dest='InteractiveShell.pprint', default=NoConfigDefault,
191 help="Disable auto auto pretty printing of results.")
191 help="Disable auto auto pretty printing of results.")
192 ),
192 ),
193 (('-prompt_in1','-pi1'), dict(
193 (('-prompt_in1','-pi1'), dict(
194 type=str, dest='InteractiveShell.prompt_in1', default=NoConfigDefault,
194 type=str, dest='InteractiveShell.prompt_in1', default=NoConfigDefault,
195 help="Set the main input prompt ('In [\#]: ')",
195 help="Set the main input prompt ('In [\#]: ')",
196 metavar='InteractiveShell.prompt_in1')
196 metavar='InteractiveShell.prompt_in1')
197 ),
197 ),
198 (('-prompt_in2','-pi2'), dict(
198 (('-prompt_in2','-pi2'), dict(
199 type=str, dest='InteractiveShell.prompt_in2', default=NoConfigDefault,
199 type=str, dest='InteractiveShell.prompt_in2', default=NoConfigDefault,
200 help="Set the secondary input prompt (' .\D.: ')",
200 help="Set the secondary input prompt (' .\D.: ')",
201 metavar='InteractiveShell.prompt_in2')
201 metavar='InteractiveShell.prompt_in2')
202 ),
202 ),
203 (('-prompt_out','-po'), dict(
203 (('-prompt_out','-po'), dict(
204 type=str, dest='InteractiveShell.prompt_out', default=NoConfigDefault,
204 type=str, dest='InteractiveShell.prompt_out', default=NoConfigDefault,
205 help="Set the output prompt ('Out[\#]:')",
205 help="Set the output prompt ('Out[\#]:')",
206 metavar='InteractiveShell.prompt_out')
206 metavar='InteractiveShell.prompt_out')
207 ),
207 ),
208 (('-quick',), dict(
208 (('-quick',), dict(
209 action='store_true', dest='Global.quick', default=NoConfigDefault,
209 action='store_true', dest='Global.quick', default=NoConfigDefault,
210 help="Enable quick startup with no config files.")
210 help="Enable quick startup with no config files.")
211 ),
211 ),
212 (('-readline',), dict(
212 (('-readline',), dict(
213 action='store_true', dest='InteractiveShell.readline_use', default=NoConfigDefault,
213 action='store_true', dest='InteractiveShell.readline_use', default=NoConfigDefault,
214 help="Enable readline for command line usage.")
214 help="Enable readline for command line usage.")
215 ),
215 ),
216 (('-noreadline',), dict(
216 (('-noreadline',), dict(
217 action='store_false', dest='InteractiveShell.readline_use', default=NoConfigDefault,
217 action='store_false', dest='InteractiveShell.readline_use', default=NoConfigDefault,
218 help="Disable readline for command line usage.")
218 help="Disable readline for command line usage.")
219 ),
219 ),
220 (('-screen_length','-sl'), dict(
220 (('-screen_length','-sl'), dict(
221 type=int, dest='InteractiveShell.screen_length', default=NoConfigDefault,
221 type=int, dest='InteractiveShell.screen_length', default=NoConfigDefault,
222 help='Number of lines on screen, used to control printing of long strings.',
222 help='Number of lines on screen, used to control printing of long strings.',
223 metavar='InteractiveShell.screen_length')
223 metavar='InteractiveShell.screen_length')
224 ),
224 ),
225 (('-separate_in','-si'), dict(
225 (('-separate_in','-si'), dict(
226 type=str, dest='InteractiveShell.separate_in', default=NoConfigDefault,
226 type=str, dest='InteractiveShell.separate_in', default=NoConfigDefault,
227 help="Separator before input prompts. Default '\n'.",
227 help="Separator before input prompts. Default '\n'.",
228 metavar='InteractiveShell.separate_in')
228 metavar='InteractiveShell.separate_in')
229 ),
229 ),
230 (('-separate_out','-so'), dict(
230 (('-separate_out','-so'), dict(
231 type=str, dest='InteractiveShell.separate_out', default=NoConfigDefault,
231 type=str, dest='InteractiveShell.separate_out', default=NoConfigDefault,
232 help="Separator before output prompts. Default 0 (nothing).",
232 help="Separator before output prompts. Default 0 (nothing).",
233 metavar='InteractiveShell.separate_out')
233 metavar='InteractiveShell.separate_out')
234 ),
234 ),
235 (('-separate_out2','-so2'), dict(
235 (('-separate_out2','-so2'), dict(
236 type=str, dest='InteractiveShell.separate_out2', default=NoConfigDefault,
236 type=str, dest='InteractiveShell.separate_out2', default=NoConfigDefault,
237 help="Separator after output prompts. Default 0 (nonight).",
237 help="Separator after output prompts. Default 0 (nonight).",
238 metavar='InteractiveShell.separate_out2')
238 metavar='InteractiveShell.separate_out2')
239 ),
239 ),
240 (('-nosep',), dict(
240 (('-nosep',), dict(
241 action='store_true', dest='Global.nosep', default=NoConfigDefault,
241 action='store_true', dest='Global.nosep', default=NoConfigDefault,
242 help="Eliminate all spacing between prompts.")
242 help="Eliminate all spacing between prompts.")
243 ),
243 ),
244 (('-term_title',), dict(
244 (('-term_title',), dict(
245 action='store_true', dest='InteractiveShell.term_title', default=NoConfigDefault,
245 action='store_true', dest='InteractiveShell.term_title', default=NoConfigDefault,
246 help="Enable auto setting the terminal title.")
246 help="Enable auto setting the terminal title.")
247 ),
247 ),
248 (('-noterm_title',), dict(
248 (('-noterm_title',), dict(
249 action='store_false', dest='InteractiveShell.term_title', default=NoConfigDefault,
249 action='store_false', dest='InteractiveShell.term_title', default=NoConfigDefault,
250 help="Disable auto setting the terminal title.")
250 help="Disable auto setting the terminal title.")
251 ),
251 ),
252 (('-xmode',), dict(
252 (('-xmode',), dict(
253 type=str, dest='InteractiveShell.xmode', default=NoConfigDefault,
253 type=str, dest='InteractiveShell.xmode', default=NoConfigDefault,
254 help="Exception mode ('Plain','Context','Verbose')",
254 help="Exception mode ('Plain','Context','Verbose')",
255 metavar='InteractiveShell.xmode')
255 metavar='InteractiveShell.xmode')
256 ),
256 ),
257 (('-ext',), dict(
257 (('-ext',), dict(
258 type=str, dest='Global.extra_extension', default=NoConfigDefault,
258 type=str, dest='Global.extra_extension', default=NoConfigDefault,
259 help="The dotted module name of an IPython extension to load.",
259 help="The dotted module name of an IPython extension to load.",
260 metavar='Global.extra_extension')
260 metavar='Global.extra_extension')
261 ),
261 ),
262 (('-c',), dict(
262 (('-c',), dict(
263 type=str, dest='Global.code_to_run', default=NoConfigDefault,
263 type=str, dest='Global.code_to_run', default=NoConfigDefault,
264 help="Execute the given command string.",
264 help="Execute the given command string.",
265 metavar='Global.code_to_run')
265 metavar='Global.code_to_run')
266 ),
266 ),
267 (('-i',), dict(
267 (('-i',), dict(
268 action='store_true', dest='Global.force_interact', default=NoConfigDefault,
268 action='store_true', dest='Global.force_interact', default=NoConfigDefault,
269 help="If running code from the command line, become interactive afterwards.")
269 help="If running code from the command line, become interactive afterwards.")
270 ),
270 ),
271 (('-wthread',), dict(
271 (('-wthread',), dict(
272 action='store_true', dest='Global.wthread', default=NoConfigDefault,
272 action='store_true', dest='Global.wthread', default=NoConfigDefault,
273 help="Enable wxPython event loop integration.")
273 help="Enable wxPython event loop integration.")
274 ),
274 ),
275 (('-q4thread','-qthread'), dict(
275 (('-q4thread','-qthread'), dict(
276 action='store_true', dest='Global.q4thread', default=NoConfigDefault,
276 action='store_true', dest='Global.q4thread', default=NoConfigDefault,
277 help="Enable Qt4 event loop integration. Qt3 is no longer supported.")
277 help="Enable Qt4 event loop integration. Qt3 is no longer supported.")
278 ),
278 ),
279 (('-gthread',), dict(
279 (('-gthread',), dict(
280 action='store_true', dest='Global.gthread', default=NoConfigDefault,
280 action='store_true', dest='Global.gthread', default=NoConfigDefault,
281 help="Enable GTK event loop integration.")
281 help="Enable GTK event loop integration.")
282 ),
282 ),
283 # # These are only here to get the proper deprecation warnings
283 # # These are only here to get the proper deprecation warnings
284 (('-pylab',), dict(
284 (('-pylab',), dict(
285 action='store_true', dest='Global.pylab', default=NoConfigDefault,
285 action='store_true', dest='Global.pylab', default=NoConfigDefault,
286 help="Disabled. Pylab has been disabled until matplotlib "
286 help="Disabled. Pylab has been disabled until matplotlib "
287 "supports this version of IPython.")
287 "supports this version of IPython.")
288 )
288 )
289 )
289 )
290
290
291
291
292 class IPythonAppCLConfigLoader(BaseAppArgParseConfigLoader):
292 class IPythonAppCLConfigLoader(BaseAppArgParseConfigLoader):
293
293
294 arguments = cl_args
294 arguments = cl_args
295
295
296
296
297 _default_config_file_name = 'ipython_config.py'
297 default_config_file_name = 'ipython_config.py'
298
298
299
299
300 class IPythonApp(Application):
300 class IPythonApp(Application):
301 name = 'ipython'
301 name = 'ipython'
302 description = 'IPython: an enhanced interactive Python shell.'
302 description = 'IPython: an enhanced interactive Python shell.'
303 config_file_name = _default_config_file_name
303 config_file_name = default_config_file_name
304
304
305 def create_default_config(self):
305 def create_default_config(self):
306 super(IPythonApp, self).create_default_config()
306 super(IPythonApp, self).create_default_config()
307 self.default_config.Global.display_banner = True
307 self.default_config.Global.display_banner = True
308
308
309 # If the -c flag is given or a file is given to run at the cmd line
309 # If the -c flag is given or a file is given to run at the cmd line
310 # like "ipython foo.py", normally we exit without starting the main
310 # like "ipython foo.py", normally we exit without starting the main
311 # loop. The force_interact config variable allows a user to override
311 # loop. The force_interact config variable allows a user to override
312 # this and interact. It is also set by the -i cmd line flag, just
312 # this and interact. It is also set by the -i cmd line flag, just
313 # like Python.
313 # like Python.
314 self.default_config.Global.force_interact = False
314 self.default_config.Global.force_interact = False
315
315
316 # By default always interact by starting the IPython mainloop.
316 # By default always interact by starting the IPython mainloop.
317 self.default_config.Global.interact = True
317 self.default_config.Global.interact = True
318
318
319 # No GUI integration by default
319 # No GUI integration by default
320 self.default_config.Global.wthread = False
320 self.default_config.Global.wthread = False
321 self.default_config.Global.q4thread = False
321 self.default_config.Global.q4thread = False
322 self.default_config.Global.gthread = False
322 self.default_config.Global.gthread = False
323
323
324 def create_command_line_config(self):
324 def create_command_line_config(self):
325 """Create and return a command line config loader."""
325 """Create and return a command line config loader."""
326 return IPythonAppCLConfigLoader(
326 return IPythonAppCLConfigLoader(
327 description=self.description,
327 description=self.description,
328 version=release.version
328 version=release.version
329 )
329 )
330
330
331 def post_load_command_line_config(self):
331 def post_load_command_line_config(self):
332 """Do actions after loading cl config."""
332 """Do actions after loading cl config."""
333 clc = self.command_line_config
333 clc = self.command_line_config
334
334
335 # Display the deprecation warnings about threaded shells
335 # Display the deprecation warnings about threaded shells
336 if hasattr(clc.Global, 'pylab'):
336 if hasattr(clc.Global, 'pylab'):
337 pylab_warning()
337 pylab_warning()
338 del clc.Global['pylab']
338 del clc.Global['pylab']
339
339
340 def load_file_config(self):
340 def load_file_config(self):
341 if hasattr(self.command_line_config.Global, 'quick'):
341 if hasattr(self.command_line_config.Global, 'quick'):
342 if self.command_line_config.Global.quick:
342 if self.command_line_config.Global.quick:
343 self.file_config = Config()
343 self.file_config = Config()
344 return
344 return
345 super(IPythonApp, self).load_file_config()
345 super(IPythonApp, self).load_file_config()
346
346
347 def post_load_file_config(self):
347 def post_load_file_config(self):
348 if hasattr(self.command_line_config.Global, 'extra_extension'):
348 if hasattr(self.command_line_config.Global, 'extra_extension'):
349 if not hasattr(self.file_config.Global, 'extensions'):
349 if not hasattr(self.file_config.Global, 'extensions'):
350 self.file_config.Global.extensions = []
350 self.file_config.Global.extensions = []
351 self.file_config.Global.extensions.append(
351 self.file_config.Global.extensions.append(
352 self.command_line_config.Global.extra_extension)
352 self.command_line_config.Global.extra_extension)
353 del self.command_line_config.Global.extra_extension
353 del self.command_line_config.Global.extra_extension
354
354
355 def pre_construct(self):
355 def pre_construct(self):
356 config = self.master_config
356 config = self.master_config
357
357
358 if hasattr(config.Global, 'classic'):
358 if hasattr(config.Global, 'classic'):
359 if config.Global.classic:
359 if config.Global.classic:
360 config.InteractiveShell.cache_size = 0
360 config.InteractiveShell.cache_size = 0
361 config.InteractiveShell.pprint = 0
361 config.InteractiveShell.pprint = 0
362 config.InteractiveShell.prompt_in1 = '>>> '
362 config.InteractiveShell.prompt_in1 = '>>> '
363 config.InteractiveShell.prompt_in2 = '... '
363 config.InteractiveShell.prompt_in2 = '... '
364 config.InteractiveShell.prompt_out = ''
364 config.InteractiveShell.prompt_out = ''
365 config.InteractiveShell.separate_in = \
365 config.InteractiveShell.separate_in = \
366 config.InteractiveShell.separate_out = \
366 config.InteractiveShell.separate_out = \
367 config.InteractiveShell.separate_out2 = ''
367 config.InteractiveShell.separate_out2 = ''
368 config.InteractiveShell.colors = 'NoColor'
368 config.InteractiveShell.colors = 'NoColor'
369 config.InteractiveShell.xmode = 'Plain'
369 config.InteractiveShell.xmode = 'Plain'
370
370
371 if hasattr(config.Global, 'nosep'):
371 if hasattr(config.Global, 'nosep'):
372 if config.Global.nosep:
372 if config.Global.nosep:
373 config.InteractiveShell.separate_in = \
373 config.InteractiveShell.separate_in = \
374 config.InteractiveShell.separate_out = \
374 config.InteractiveShell.separate_out = \
375 config.InteractiveShell.separate_out2 = ''
375 config.InteractiveShell.separate_out2 = ''
376
376
377 # if there is code of files to run from the cmd line, don't interact
377 # if there is code of files to run from the cmd line, don't interact
378 # unless the -i flag (Global.force_interact) is true.
378 # unless the -i flag (Global.force_interact) is true.
379 code_to_run = config.Global.get('code_to_run','')
379 code_to_run = config.Global.get('code_to_run','')
380 file_to_run = False
380 file_to_run = False
381 if len(self.extra_args)>=1:
381 if len(self.extra_args)>=1:
382 if self.extra_args[0]:
382 if self.extra_args[0]:
383 file_to_run = True
383 file_to_run = True
384 if file_to_run or code_to_run:
384 if file_to_run or code_to_run:
385 if not config.Global.force_interact:
385 if not config.Global.force_interact:
386 config.Global.interact = False
386 config.Global.interact = False
387
387
388 def construct(self):
388 def construct(self):
389 # I am a little hesitant to put these into InteractiveShell itself.
389 # I am a little hesitant to put these into InteractiveShell itself.
390 # But that might be the place for them
390 # But that might be the place for them
391 sys.path.insert(0, '')
391 sys.path.insert(0, '')
392
392
393 # Create an InteractiveShell instance
393 # Create an InteractiveShell instance
394 self.shell = InteractiveShell(
394 self.shell = InteractiveShell(
395 parent=None,
395 parent=None,
396 config=self.master_config
396 config=self.master_config
397 )
397 )
398
398
399 def post_construct(self):
399 def post_construct(self):
400 """Do actions after construct, but before starting the app."""
400 """Do actions after construct, but before starting the app."""
401 config = self.master_config
401 config = self.master_config
402
402
403 # shell.display_banner should always be False for the terminal
403 # shell.display_banner should always be False for the terminal
404 # based app, because we call shell.show_banner() by hand below
404 # based app, because we call shell.show_banner() by hand below
405 # so the banner shows *before* all extension loading stuff.
405 # so the banner shows *before* all extension loading stuff.
406 self.shell.display_banner = False
406 self.shell.display_banner = False
407
407
408 if config.Global.display_banner and \
408 if config.Global.display_banner and \
409 config.Global.interact:
409 config.Global.interact:
410 self.shell.show_banner()
410 self.shell.show_banner()
411
411
412 # Make sure there is a space below the banner.
412 # Make sure there is a space below the banner.
413 if self.log_level <= logging.INFO: print
413 if self.log_level <= logging.INFO: print
414
414
415 # Now a variety of things that happen after the banner is printed.
415 # Now a variety of things that happen after the banner is printed.
416 self._enable_gui()
416 self._enable_gui()
417 self._load_extensions()
417 self._load_extensions()
418 self._run_exec_lines()
418 self._run_exec_lines()
419 self._run_exec_files()
419 self._run_exec_files()
420 self._run_cmd_line_code()
420 self._run_cmd_line_code()
421
421
422 def _enable_gui(self):
422 def _enable_gui(self):
423 """Enable GUI event loop integration."""
423 """Enable GUI event loop integration."""
424 config = self.master_config
424 config = self.master_config
425 try:
425 try:
426 # Enable GUI integration
426 # Enable GUI integration
427 if config.Global.wthread:
427 if config.Global.wthread:
428 self.log.info("Enabling wx GUI event loop integration")
428 self.log.info("Enabling wx GUI event loop integration")
429 inputhook.enable_wx(app=True)
429 inputhook.enable_wx(app=True)
430 elif config.Global.q4thread:
430 elif config.Global.q4thread:
431 self.log.info("Enabling Qt4 GUI event loop integration")
431 self.log.info("Enabling Qt4 GUI event loop integration")
432 inputhook.enable_qt4(app=True)
432 inputhook.enable_qt4(app=True)
433 elif config.Global.gthread:
433 elif config.Global.gthread:
434 self.log.info("Enabling GTK GUI event loop integration")
434 self.log.info("Enabling GTK GUI event loop integration")
435 inputhook.enable_gtk(app=True)
435 inputhook.enable_gtk(app=True)
436 except:
436 except:
437 self.log.warn("Error in enabling GUI event loop integration:")
437 self.log.warn("Error in enabling GUI event loop integration:")
438 self.shell.showtraceback()
438 self.shell.showtraceback()
439
439
440 def _load_extensions(self):
440 def _load_extensions(self):
441 """Load all IPython extensions in Global.extensions.
441 """Load all IPython extensions in Global.extensions.
442
442
443 This uses the :meth:`InteractiveShell.load_extensions` to load all
443 This uses the :meth:`InteractiveShell.load_extensions` to load all
444 the extensions listed in ``self.master_config.Global.extensions``.
444 the extensions listed in ``self.master_config.Global.extensions``.
445 """
445 """
446 try:
446 try:
447 if hasattr(self.master_config.Global, 'extensions'):
447 if hasattr(self.master_config.Global, 'extensions'):
448 self.log.debug("Loading IPython extensions...")
448 self.log.debug("Loading IPython extensions...")
449 extensions = self.master_config.Global.extensions
449 extensions = self.master_config.Global.extensions
450 for ext in extensions:
450 for ext in extensions:
451 try:
451 try:
452 self.log.info("Loading IPython extension: %s" % ext)
452 self.log.info("Loading IPython extension: %s" % ext)
453 self.shell.load_extension(ext)
453 self.shell.load_extension(ext)
454 except:
454 except:
455 self.log.warn("Error in loading extension: %s" % ext)
455 self.log.warn("Error in loading extension: %s" % ext)
456 self.shell.showtraceback()
456 self.shell.showtraceback()
457 except:
457 except:
458 self.log.warn("Unknown error in loading extensions:")
458 self.log.warn("Unknown error in loading extensions:")
459 self.shell.showtraceback()
459 self.shell.showtraceback()
460
460
461 def _run_exec_lines(self):
461 def _run_exec_lines(self):
462 """Run lines of code in Global.exec_lines in the user's namespace."""
462 """Run lines of code in Global.exec_lines in the user's namespace."""
463 try:
463 try:
464 if hasattr(self.master_config.Global, 'exec_lines'):
464 if hasattr(self.master_config.Global, 'exec_lines'):
465 self.log.debug("Running code from Global.exec_lines...")
465 self.log.debug("Running code from Global.exec_lines...")
466 exec_lines = self.master_config.Global.exec_lines
466 exec_lines = self.master_config.Global.exec_lines
467 for line in exec_lines:
467 for line in exec_lines:
468 try:
468 try:
469 self.log.info("Running code in user namespace: %s" % line)
469 self.log.info("Running code in user namespace: %s" % line)
470 self.shell.runlines(line)
470 self.shell.runlines(line)
471 except:
471 except:
472 self.log.warn("Error in executing line in user namespace: %s" % line)
472 self.log.warn("Error in executing line in user namespace: %s" % line)
473 self.shell.showtraceback()
473 self.shell.showtraceback()
474 except:
474 except:
475 self.log.warn("Unknown error in handling Global.exec_lines:")
475 self.log.warn("Unknown error in handling Global.exec_lines:")
476 self.shell.showtraceback()
476 self.shell.showtraceback()
477
477
478 def _exec_file(self, fname):
478 def _exec_file(self, fname):
479 full_filename = filefind(fname, ['.', self.ipythondir])
479 full_filename = filefind(fname, ['.', self.ipythondir])
480 if os.path.isfile(full_filename):
480 if os.path.isfile(full_filename):
481 if full_filename.endswith('.py'):
481 if full_filename.endswith('.py'):
482 self.log.info("Running file in user namespace: %s" % full_filename)
482 self.log.info("Running file in user namespace: %s" % full_filename)
483 self.shell.safe_execfile(full_filename, self.shell.user_ns)
483 self.shell.safe_execfile(full_filename, self.shell.user_ns)
484 elif full_filename.endswith('.ipy'):
484 elif full_filename.endswith('.ipy'):
485 self.log.info("Running file in user namespace: %s" % full_filename)
485 self.log.info("Running file in user namespace: %s" % full_filename)
486 self.shell.safe_execfile_ipy(full_filename)
486 self.shell.safe_execfile_ipy(full_filename)
487 else:
487 else:
488 self.log.warn("File does not have a .py or .ipy extension: <%s>" % full_filename)
488 self.log.warn("File does not have a .py or .ipy extension: <%s>" % full_filename)
489
489
490 def _run_exec_files(self):
490 def _run_exec_files(self):
491 try:
491 try:
492 if hasattr(self.master_config.Global, 'exec_files'):
492 if hasattr(self.master_config.Global, 'exec_files'):
493 self.log.debug("Running files in Global.exec_files...")
493 self.log.debug("Running files in Global.exec_files...")
494 exec_files = self.master_config.Global.exec_files
494 exec_files = self.master_config.Global.exec_files
495 for fname in exec_files:
495 for fname in exec_files:
496 self._exec_file(fname)
496 self._exec_file(fname)
497 except:
497 except:
498 self.log.warn("Unknown error in handling Global.exec_files:")
498 self.log.warn("Unknown error in handling Global.exec_files:")
499 self.shell.showtraceback()
499 self.shell.showtraceback()
500
500
501 def _run_cmd_line_code(self):
501 def _run_cmd_line_code(self):
502 if hasattr(self.master_config.Global, 'code_to_run'):
502 if hasattr(self.master_config.Global, 'code_to_run'):
503 line = self.master_config.Global.code_to_run
503 line = self.master_config.Global.code_to_run
504 try:
504 try:
505 self.log.info("Running code given at command line (-c): %s" % line)
505 self.log.info("Running code given at command line (-c): %s" % line)
506 self.shell.runlines(line)
506 self.shell.runlines(line)
507 except:
507 except:
508 self.log.warn("Error in executing line in user namespace: %s" % line)
508 self.log.warn("Error in executing line in user namespace: %s" % line)
509 self.shell.showtraceback()
509 self.shell.showtraceback()
510 return
510 return
511 # Like Python itself, ignore the second if the first of these is present
511 # Like Python itself, ignore the second if the first of these is present
512 try:
512 try:
513 fname = self.extra_args[0]
513 fname = self.extra_args[0]
514 except:
514 except:
515 pass
515 pass
516 else:
516 else:
517 try:
517 try:
518 self._exec_file(fname)
518 self._exec_file(fname)
519 except:
519 except:
520 self.log.warn("Error in executing file in user namespace: %s" % fname)
520 self.log.warn("Error in executing file in user namespace: %s" % fname)
521 self.shell.showtraceback()
521 self.shell.showtraceback()
522
522
523 def start_app(self):
523 def start_app(self):
524 if self.master_config.Global.interact:
524 if self.master_config.Global.interact:
525 self.log.debug("Starting IPython's mainloop...")
525 self.log.debug("Starting IPython's mainloop...")
526 self.shell.mainloop()
526 self.shell.mainloop()
527
527
528
528
529 def load_default_config(ipythondir=None):
529 def load_default_config(ipythondir=None):
530 """Load the default config file from the default ipythondir.
530 """Load the default config file from the default ipythondir.
531
531
532 This is useful for embedded shells.
532 This is useful for embedded shells.
533 """
533 """
534 if ipythondir is None:
534 if ipythondir is None:
535 ipythondir = get_ipython_dir()
535 ipythondir = get_ipython_dir()
536 cl = PyFileConfigLoader(_default_config_file_name, ipythondir)
536 cl = PyFileConfigLoader(default_config_file_name, ipythondir)
537 config = cl.load_config()
537 config = cl.load_config()
538 return config
538 return config
539
539
540
540
541 def launch_new_instance():
541 def launch_new_instance():
542 """Create and run a full blown IPython instance"""
542 """Create and run a full blown IPython instance"""
543 app = IPythonApp()
543 app = IPythonApp()
544 app.start()
544 app.start()
545
545
@@ -1,75 +1,79 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 A class for creating a Twisted service that is configured using IPython's
4 A class for creating a Twisted service that is configured using IPython's
5 configuration system.
5 configuration system.
6 """
6 """
7
7
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2008-2009 The IPython Development Team
9 # Copyright (C) 2008-2009 The IPython Development Team
10 #
10 #
11 # Distributed under the terms of the BSD License. The full license is in
11 # Distributed under the terms of the BSD License. The full license is in
12 # the file COPYING, distributed as part of this software.
12 # the file COPYING, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 import zope.interface as zi
19 import zope.interface as zi
20
20
21 from IPython.core.component import Component
21 from IPython.core.component import Component
22
22
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24 # Code
24 # Code
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26
26
27
27
28 class IConfiguredObjectFactory(zi.Interface):
28 class IConfiguredObjectFactory(zi.Interface):
29 """I am a component that creates a configured object.
29 """I am a component that creates a configured object.
30
30
31 This class is useful if you want to configure a class that is not a
31 This class is useful if you want to configure a class that is not a
32 subclass of :class:`IPython.core.component.Component`.
32 subclass of :class:`IPython.core.component.Component`.
33 """
33 """
34
34
35 def __init__(config):
35 def __init__(config):
36 """Get ready to configure the object using config."""
36 """Get ready to configure the object using config."""
37
37
38 def create():
38 def create():
39 """Return an instance of the configured object."""
39 """Return an instance of the configured object."""
40
40
41
41
42 class ConfiguredObjectFactory(Component):
42 class ConfiguredObjectFactory(Component):
43
43
44 zi.implements(IConfiguredObjectFactory)
44 zi.implements(IConfiguredObjectFactory)
45
45
46 def __init__(self, config):
46 def __init__(self, config):
47 super(ConfiguredObjectFactory, self).__init__(None, config=config)
47 super(ConfiguredObjectFactory, self).__init__(None, config=config)
48
48
49 def create(self):
49 def create(self):
50 raise NotImplementedError('create must be implemented in a subclass')
50 raise NotImplementedError('create must be implemented in a subclass')
51
51
52
52
53 class IAdaptedConfiguredObjectFactory(zi.Interface):
53 class IAdaptedConfiguredObjectFactory(zi.Interface):
54 """I am a component that adapts and configures an object.
54 """I am a component that adapts and configures an object.
55
55
56 This class is useful if you have the adapt a instance and configure it.
56 This class is useful if you have the adapt a instance and configure it.
57 """
57 """
58
58
59 def __init__(config, adaptee=None):
59 def __init__(config, adaptee=None):
60 """Get ready to adapt adaptee and then configure it using config."""
60 """Get ready to adapt adaptee and then configure it using config."""
61
61
62 def create():
62 def create():
63 """Return an instance of the adapted and configured object."""
63 """Return an instance of the adapted and configured object."""
64
64
65
65
66 class AdaptedConfiguredObjectFactory(Component):
66 class AdaptedConfiguredObjectFactory(Component):
67
67
68 # zi.implements(IAdaptedConfiguredObjectFactory)
68 # zi.implements(IAdaptedConfiguredObjectFactory)
69
69
70 def __init__(self, config, adaptee):
70 def __init__(self, config, adaptee):
71 # print
72 # print "config pre:", config
71 super(AdaptedConfiguredObjectFactory, self).__init__(None, config=config)
73 super(AdaptedConfiguredObjectFactory, self).__init__(None, config=config)
74 # print
75 # print "config post:", config
72 self.adaptee = adaptee
76 self.adaptee = adaptee
73
77
74 def create(self):
78 def create(self):
75 raise NotImplementedError('create must be implemented in a subclass') No newline at end of file
79 raise NotImplementedError('create must be implemented in a subclass')
@@ -1,228 +1,236 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 Foolscap related utilities.
4 Foolscap related utilities.
5 """
5 """
6
6
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
8 # Copyright (C) 2008-2009 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 import os
18 import os
19 import tempfile
19 import tempfile
20
20
21 from twisted.internet import reactor, defer
21 from twisted.internet import reactor, defer
22 from twisted.python import log
22 from twisted.python import log
23
23
24 from foolscap import Tub, UnauthenticatedTub
24 from foolscap import Tub, UnauthenticatedTub
25
25
26 from IPython.config.loader import Config
26 from IPython.config.loader import Config
27
27
28 from IPython.kernel.configobjfactory import AdaptedConfiguredObjectFactory
28 from IPython.kernel.configobjfactory import AdaptedConfiguredObjectFactory
29
29
30 from IPython.kernel.error import SecurityError
30 from IPython.kernel.error import SecurityError
31
31
32 from IPython.utils.traitlets import Int, Str, Bool, Instance
32 from IPython.utils.traitlets import Int, Str, Bool, Instance
33 from IPython.utils.importstring import import_item
33 from IPython.utils.importstring import import_item
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Code
36 # Code
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38
38
39
39
40 # We do this so if a user doesn't have OpenSSL installed, it will try to use
40 # We do this so if a user doesn't have OpenSSL installed, it will try to use
41 # an UnauthenticatedTub. But, they will still run into problems if they
41 # an UnauthenticatedTub. But, they will still run into problems if they
42 # try to use encrypted furls.
42 # try to use encrypted furls.
43 try:
43 try:
44 import OpenSSL
44 import OpenSSL
45 except:
45 except:
46 Tub = UnauthenticatedTub
46 Tub = UnauthenticatedTub
47 have_crypto = False
47 have_crypto = False
48 else:
48 else:
49 have_crypto = True
49 have_crypto = True
50
50
51
51
52 def check_furl_file_security(furl_file, secure):
52 def check_furl_file_security(furl_file, secure):
53 """Remove the old furl_file if changing security modes."""
53 """Remove the old furl_file if changing security modes."""
54 if os.path.isfile(furl_file):
54 if os.path.isfile(furl_file):
55 f = open(furl_file, 'r')
55 f = open(furl_file, 'r')
56 oldfurl = f.read().strip()
56 oldfurl = f.read().strip()
57 f.close()
57 f.close()
58 if (oldfurl.startswith('pb://') and not secure) or (oldfurl.startswith('pbu://') and secure):
58 if (oldfurl.startswith('pb://') and not secure) or (oldfurl.startswith('pbu://') and secure):
59 os.remove(furl_file)
59 os.remove(furl_file)
60
60
61
61
62 def is_secure(furl):
62 def is_secure(furl):
63 """Is the given FURL secure or not."""
63 """Is the given FURL secure or not."""
64 if is_valid(furl):
64 if is_valid(furl):
65 if furl.startswith("pb://"):
65 if furl.startswith("pb://"):
66 return True
66 return True
67 elif furl.startswith("pbu://"):
67 elif furl.startswith("pbu://"):
68 return False
68 return False
69 else:
69 else:
70 raise ValueError("invalid furl: %s" % furl)
70 raise ValueError("invalid FURL: %s" % furl)
71
71
72
72
73 def is_valid(furl):
73 def is_valid(furl):
74 """Is the str a valid furl or not."""
74 """Is the str a valid FURL or not."""
75 if isinstance(furl, str):
75 if isinstance(furl, str):
76 if furl.startswith("pb://") or furl.startswith("pbu://"):
76 if furl.startswith("pb://") or furl.startswith("pbu://"):
77 return True
77 return True
78 else:
78 else:
79 return False
79 return False
80
80
81
81
82 def find_furl(furl_or_file):
82 def find_furl(furl_or_file):
83 """Find, validate and return a FURL in a string or file."""
83 """Find, validate and return a FURL in a string or file."""
84 if isinstance(furl_or_file, str):
84 if isinstance(furl_or_file, str):
85 if is_valid(furl_or_file):
85 if is_valid(furl_or_file):
86 return furl_or_file
86 return furl_or_file
87 if os.path.isfile(furl_or_file):
87 if os.path.isfile(furl_or_file):
88 furl = open(furl_or_file, 'r').read().strip()
88 furl = open(furl_or_file, 'r').read().strip()
89 if is_valid(furl):
89 if is_valid(furl):
90 return furl
90 return furl
91 raise ValueError("not a furl or a file containing a furl: %s" % furl_or_file)
91 raise ValueError("not a FURL or a file containing a FURL: %s" % furl_or_file)
92
92
93
93
94 def get_temp_furlfile(filename):
94 def get_temp_furlfile(filename):
95 """Return a temporary furl file."""
95 """Return a temporary FURL file."""
96 return tempfile.mktemp(dir=os.path.dirname(filename),
96 return tempfile.mktemp(dir=os.path.dirname(filename),
97 prefix=os.path.basename(filename))
97 prefix=os.path.basename(filename))
98
98
99
99
100 def make_tub(ip, port, secure, cert_file):
100 def make_tub(ip, port, secure, cert_file):
101 """Create a listening tub given an ip, port, and cert_file location.
101 """Create a listening tub given an ip, port, and cert_file location.
102
102
103 Parameters
103 Parameters
104 ----------
104 ----------
105 ip : str
105 ip : str
106 The ip address or hostname that the tub should listen on.
106 The ip address or hostname that the tub should listen on.
107 Empty means all interfaces.
107 Empty means all interfaces.
108 port : int
108 port : int
109 The port that the tub should listen on. A value of 0 means
109 The port that the tub should listen on. A value of 0 means
110 pick a random port
110 pick a random port
111 secure: bool
111 secure: bool
112 Will the connection be secure (in the Foolscap sense).
112 Will the connection be secure (in the Foolscap sense).
113 cert_file: str
113 cert_file: str
114 A filename of a file to be used for theSSL certificate.
114 A filename of a file to be used for theSSL certificate.
115
115
116 Returns
116 Returns
117 -------
117 -------
118 A tub, listener tuple.
118 A tub, listener tuple.
119 """
119 """
120 if secure:
120 if secure:
121 if have_crypto:
121 if have_crypto:
122 tub = Tub(certFile=cert_file)
122 tub = Tub(certFile=cert_file)
123 else:
123 else:
124 raise SecurityError("OpenSSL/pyOpenSSL is not available, so we "
124 raise SecurityError("OpenSSL/pyOpenSSL is not available, so we "
125 "can't run in secure mode. Try running without "
125 "can't run in secure mode. Try running without "
126 "security using 'ipcontroller -xy'.")
126 "security using 'ipcontroller -xy'.")
127 else:
127 else:
128 tub = UnauthenticatedTub()
128 tub = UnauthenticatedTub()
129
129
130 # Set the strport based on the ip and port and start listening
130 # Set the strport based on the ip and port and start listening
131 if ip == '':
131 if ip == '':
132 strport = "tcp:%i" % port
132 strport = "tcp:%i" % port
133 else:
133 else:
134 strport = "tcp:%i:interface=%s" % (port, ip)
134 strport = "tcp:%i:interface=%s" % (port, ip)
135 log.msg("Starting listener with [secure=%r] on: %s" % (secure, strport))
135 listener = tub.listenOn(strport)
136 listener = tub.listenOn(strport)
136
137
137 return tub, listener
138 return tub, listener
138
139
139
140
140 class FCServiceFactory(AdaptedConfiguredObjectFactory):
141 class FCServiceFactory(AdaptedConfiguredObjectFactory):
141 """This class creates a tub with various services running in it.
142 """This class creates a tub with various services running in it.
142
143
143 The basic idea is that :meth:`create` returns a running :class:`Tub`
144 The basic idea is that :meth:`create` returns a running :class:`Tub`
144 instance that has a number of Foolscap references registered in it.
145 instance that has a number of Foolscap references registered in it.
145 This class is a subclass of :class:`IPython.core.component.Component`
146 This class is a subclass of :class:`IPython.core.component.Component`
146 so the IPython configuration and component system are used.
147 so the IPython configuration and component system are used.
147
148
148 Attributes
149 Attributes
149 ----------
150 ----------
150 Interfaces : Config
151 interfaces : Config
151 A Config instance whose values are sub-Config objects having two
152 A Config instance whose values are sub-Config objects having two
152 keys: furl_file and interface_chain.
153 keys: furl_file and interface_chain.
153
154
154 The other attributes are the standard ones for Foolscap.
155 The other attributes are the standard ones for Foolscap.
155 """
156 """
156
157
157 ip = Str('', config=True)
158 ip = Str('', config=True)
158 port = Int(0, config=True)
159 port = Int(0, config=True)
159 secure = Bool(True, config=True)
160 secure = Bool(True, config=True)
160 cert_file = Str('', config=True)
161 cert_file = Str('', config=True)
161 location = Str('', config=True)
162 location = Str('', config=True)
162 reuse_furls = Bool(False, config=True)
163 reuse_furls = Bool(False, config=True)
163 Interfaces = Instance(klass=Config, kw={}, allow_none=False, config=True)
164 interfaces = Instance(klass=Config, kw={}, allow_none=False, config=True)
164
165
165 def __init__(self, config, adaptee):
166 def __init__(self, config, adaptee):
166 super(FCServiceFactory, self).__init__(config, adaptee)
167 super(FCServiceFactory, self).__init__(config, adaptee)
167 self._check_reuse_furls()
168 self._check_reuse_furls()
168
169
169 def _ip_changed(self, name, old, new):
170 def _ip_changed(self, name, old, new):
170 if new == 'localhost' or new == '127.0.0.1':
171 if new == 'localhost' or new == '127.0.0.1':
171 self.location = '127.0.0.1'
172 self.location = '127.0.0.1'
172
173
173 def _check_reuse_furls(self):
174 def _check_reuse_furls(self):
174 if not self.reuse_furls:
175 furl_files = [i.furl_file for i in self.interfaces.values()]
175 furl_files = [i.furl_file for i in self.Interfaces.values()]
176 for ff in furl_files:
176 for ff in furl_files:
177 fullfile = self._get_security_file(ff)
177 fullfile = self._get_security_file(ff)
178 if self.reuse_furls:
179 log.msg("Reusing FURL file: %s" % fullfile)
180 else:
178 if os.path.isfile(fullfile):
181 if os.path.isfile(fullfile):
182 log.msg("Removing old FURL file: %s" % fullfile)
179 os.remove(fullfile)
183 os.remove(fullfile)
180
184
181 def _get_security_file(self, filename):
185 def _get_security_file(self, filename):
182 return os.path.join(self.config.Global.security_dir, filename)
186 return os.path.join(self.config.Global.security_dir, filename)
183
187
184 def create(self):
188 def create(self):
185 """Create and return the Foolscap tub with everything running."""
189 """Create and return the Foolscap tub with everything running."""
186
190
187 self.tub, self.listener = make_tub(
191 self.tub, self.listener = make_tub(
188 self.ip, self.port, self.secure, self._get_security_file(self.cert_file))
192 self.ip, self.port, self.secure,
189 log.msg("Created a tub and listener [%r]: %r, %r" % (self.__class__, self.tub, self.listener))
193 self._get_security_file(self.cert_file)
190 log.msg("Interfaces to register [%r]: %r" % (self.__class__, self.Interfaces))
194 )
195 # log.msg("Interfaces to register [%r]: %r" % \
196 # (self.__class__, self.interfaces))
191 if not self.secure:
197 if not self.secure:
192 log.msg("WARNING: running with no security: %s" % self.__class__.__name__)
198 log.msg("WARNING: running with no security: %s" % \
199 self.__class__.__name__)
193 reactor.callWhenRunning(self.set_location_and_register)
200 reactor.callWhenRunning(self.set_location_and_register)
194 return self.tub
201 return self.tub
195
202
196 def set_location_and_register(self):
203 def set_location_and_register(self):
197 """Set the location for the tub and return a deferred."""
204 """Set the location for the tub and return a deferred."""
198
205
199 if self.location == '':
206 if self.location == '':
200 d = self.tub.setLocationAutomatically()
207 d = self.tub.setLocationAutomatically()
201 else:
208 else:
202 d = defer.maybeDeferred(self.tub.setLocation,
209 d = defer.maybeDeferred(self.tub.setLocation,
203 "%s:%i" % (self.location, self.listener.getPortnum()))
210 "%s:%i" % (self.location, self.listener.getPortnum()))
204 self.adapt_to_interfaces(d)
211 self.adapt_to_interfaces(d)
205
212
206 def adapt_to_interfaces(self, d):
213 def adapt_to_interfaces(self, d):
207 """Run through the interfaces, adapt and register."""
214 """Run through the interfaces, adapt and register."""
208
215
209 for ifname, ifconfig in self.Interfaces.iteritems():
216 for ifname, ifconfig in self.interfaces.iteritems():
210 ff = self._get_security_file(ifconfig.furl_file)
217 ff = self._get_security_file(ifconfig.furl_file)
211 log.msg("Adapting %r to interface: %s" % (self.adaptee, ifname))
218 log.msg("Adapting [%s] to interface: %s" % \
212 log.msg("Saving furl for interface [%s] to file: %s" % (ifname, ff))
219 (self.adaptee.__class__.__name__, ifname))
220 log.msg("Saving FURL for interface [%s] to file: %s" % (ifname, ff))
213 check_furl_file_security(ff, self.secure)
221 check_furl_file_security(ff, self.secure)
214 adaptee = self.adaptee
222 adaptee = self.adaptee
215 for i in ifconfig.interface_chain:
223 for i in ifconfig.interface_chain:
216 adaptee = import_item(i)(adaptee)
224 adaptee = import_item(i)(adaptee)
217 d.addCallback(self.register, adaptee, furl_file=ff)
225 d.addCallback(self.register, adaptee, furl_file=ff)
218
226
219 def register(self, empty, ref, furl_file):
227 def register(self, empty, ref, furl_file):
220 """Register the reference with the FURL file.
228 """Register the reference with the FURL file.
221
229
222 The FURL file is created and then moved to make sure that when the
230 The FURL file is created and then moved to make sure that when the
223 file appears, the buffer has been flushed and the file closed.
231 file appears, the buffer has been flushed and the file closed.
224 """
232 """
225 temp_furl_file = get_temp_furlfile(furl_file)
233 temp_furl_file = get_temp_furlfile(furl_file)
226 self.tub.registerReference(ref, furlFile=temp_furl_file)
234 self.tub.registerReference(ref, furlFile=temp_furl_file)
227 os.rename(temp_furl_file, furl_file)
235 os.rename(temp_furl_file, furl_file)
228
236
@@ -1,258 +1,273 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 """
3 """
4 The IPython controller application
4 The IPython controller application
5 """
5 """
6
6
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (C) 2008-2009 The IPython Development Team
8 # Copyright (C) 2008-2009 The IPython Development Team
9 #
9 #
10 # Distributed under the terms of the BSD License. The full license is in
10 # Distributed under the terms of the BSD License. The full license is in
11 # the file COPYING, distributed as part of this software.
11 # the file COPYING, distributed as part of this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 import copy
18 import copy
19 import logging
19 import logging
20 import os
20 import os
21 import sys
21 import sys
22
22
23 from twisted.application import service
23 from twisted.application import service
24 from twisted.internet import reactor, defer
24 from twisted.internet import reactor, defer
25 from twisted.python import log
25 from twisted.python import log
26
26
27 from IPython.config.loader import Config, NoConfigDefault
27 from IPython.config.loader import Config, NoConfigDefault
28
28
29 from IPython.core.application import (
29 from IPython.core.application import (
30 ApplicationWithDir,
30 ApplicationWithDir,
31 BaseAppArgParseConfigLoader
31 AppWithDirArgParseConfigLoader
32 )
32 )
33
33
34 from IPython.core import release
34 from IPython.core import release
35
35
36 from IPython.utils.traitlets import Int, Str, Bool, Instance
36 from IPython.utils.traitlets import Int, Str, Bool, Instance
37 from IPython.utils.importstring import import_item
37 from IPython.utils.importstring import import_item
38
38
39 from IPython.kernel import controllerservice
39 from IPython.kernel import controllerservice
40 from IPython.kernel.configobjfactory import (
40 from IPython.kernel.configobjfactory import (
41 ConfiguredObjectFactory,
41 ConfiguredObjectFactory,
42 AdaptedConfiguredObjectFactory
42 AdaptedConfiguredObjectFactory
43 )
43 )
44
44
45 from IPython.kernel.fcutil import FCServiceFactory
45 from IPython.kernel.fcutil import FCServiceFactory
46
46
47 #-----------------------------------------------------------------------------
47 #-----------------------------------------------------------------------------
48 # Default interfaces
48 # Default interfaces
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50
50
51
51
52 # The default client interfaces for FCClientServiceFactory.Interfaces
52 # The default client interfaces for FCClientServiceFactory.interfaces
53 default_client_interfaces = Config()
53 default_client_interfaces = Config()
54 default_client_interfaces.Task.interface_chain = [
54 default_client_interfaces.Task.interface_chain = [
55 'IPython.kernel.task.ITaskController',
55 'IPython.kernel.task.ITaskController',
56 'IPython.kernel.taskfc.IFCTaskController'
56 'IPython.kernel.taskfc.IFCTaskController'
57 ]
57 ]
58
58
59 default_client_interfaces.Task.furl_file = 'ipcontroller-tc.furl'
59 default_client_interfaces.Task.furl_file = 'ipcontroller-tc.furl'
60
60 default_client_interfaces.MultiEngine.interface_chain = [
61 default_client_interfaces.MultiEngine.interface_chain = [
61 'IPython.kernel.multiengine.IMultiEngine',
62 'IPython.kernel.multiengine.IMultiEngine',
62 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine'
63 'IPython.kernel.multienginefc.IFCSynchronousMultiEngine'
63 ]
64 ]
64
65
65 default_client_interfaces.MultiEngine.furl_file = 'ipcontroller-mec.furl'
66 default_client_interfaces.MultiEngine.furl_file = 'ipcontroller-mec.furl'
66
67
67 # Make this a dict we can pass to Config.__init__ for the default
68 # Make this a dict we can pass to Config.__init__ for the default
68 default_client_interfaces = dict(copy.deepcopy(default_client_interfaces.items()))
69 default_client_interfaces = dict(copy.deepcopy(default_client_interfaces.items()))
69
70
70
71
71
72
72 # The default engine interfaces for FCEngineServiceFactory.Interfaces
73 # The default engine interfaces for FCEngineServiceFactory.interfaces
73 default_engine_interfaces = Config()
74 default_engine_interfaces = Config()
74 default_engine_interfaces.Default.interface_chain = [
75 default_engine_interfaces.Default.interface_chain = [
75 'IPython.kernel.enginefc.IFCControllerBase'
76 'IPython.kernel.enginefc.IFCControllerBase'
76 ]
77 ]
77
78
78 default_engine_interfaces.Default.furl_file = 'ipcontroller-engine.furl'
79 default_engine_interfaces.Default.furl_file = 'ipcontroller-engine.furl'
79
80
80 # Make this a dict we can pass to Config.__init__ for the default
81 # Make this a dict we can pass to Config.__init__ for the default
81 default_engine_interfaces = dict(copy.deepcopy(default_engine_interfaces.items()))
82 default_engine_interfaces = dict(copy.deepcopy(default_engine_interfaces.items()))
82
83
83
84
84 #-----------------------------------------------------------------------------
85 #-----------------------------------------------------------------------------
85 # Service factories
86 # Service factories
86 #-----------------------------------------------------------------------------
87 #-----------------------------------------------------------------------------
87
88
88
89
89 class FCClientServiceFactory(FCServiceFactory):
90 class FCClientServiceFactory(FCServiceFactory):
90 """A Foolscap implementation of the client services."""
91 """A Foolscap implementation of the client services."""
91
92
92 cert_file = Str('ipcontroller-client.pem', config=True)
93 cert_file = Str('ipcontroller-client.pem', config=True)
93 Interfaces = Instance(klass=Config, kw=default_client_interfaces,
94 interfaces = Instance(klass=Config, kw=default_client_interfaces,
94 allow_none=False, config=True)
95 allow_none=False, config=True)
95
96
96
97
97 class FCEngineServiceFactory(FCServiceFactory):
98 class FCEngineServiceFactory(FCServiceFactory):
98 """A Foolscap implementation of the engine services."""
99 """A Foolscap implementation of the engine services."""
99
100
100 cert_file = Str('ipcontroller-engine.pem', config=True)
101 cert_file = Str('ipcontroller-engine.pem', config=True)
101 Interfaces = Instance(klass=dict, kw=default_engine_interfaces,
102 interfaces = Instance(klass=dict, kw=default_engine_interfaces,
102 allow_none=False, config=True)
103 allow_none=False, config=True)
103
104
104
105
105 #-----------------------------------------------------------------------------
106 #-----------------------------------------------------------------------------
106 # The main application
107 # The main application
107 #-----------------------------------------------------------------------------
108 #-----------------------------------------------------------------------------
108
109
109
110
110 cl_args = (
111 cl_args = (
111 # Client config
112 # Client config
112 (('--client-ip',), dict(
113 (('--client-ip',), dict(
113 type=str, dest='FCClientServiceFactory.ip', default=NoConfigDefault,
114 type=str, dest='FCClientServiceFactory.ip', default=NoConfigDefault,
114 help='The IP address or hostname the controller will listen on for client connections.',
115 help='The IP address or hostname the controller will listen on for client connections.',
115 metavar='FCClientServiceFactory.ip')
116 metavar='FCClientServiceFactory.ip')
116 ),
117 ),
117 (('--client-port',), dict(
118 (('--client-port',), dict(
118 type=int, dest='FCClientServiceFactory.port', default=NoConfigDefault,
119 type=int, dest='FCClientServiceFactory.port', default=NoConfigDefault,
119 help='The port the controller will listen on for client connections.',
120 help='The port the controller will listen on for client connections.',
120 metavar='FCClientServiceFactory.port')
121 metavar='FCClientServiceFactory.port')
121 ),
122 ),
122 (('--client-location',), dict(
123 (('--client-location',), dict(
123 type=str, dest='FCClientServiceFactory.location', default=NoConfigDefault,
124 type=str, dest='FCClientServiceFactory.location', default=NoConfigDefault,
124 help='The hostname or ip that clients should connect to.',
125 help='The hostname or ip that clients should connect to.',
125 metavar='FCClientServiceFactory.location')
126 metavar='FCClientServiceFactory.location')
126 ),
127 ),
127 (('-x',), dict(
128 action='store_false', dest='FCClientServiceFactory.secure', default=NoConfigDefault,
129 help='Turn off all client security.')
130 ),
131 # Engine config
128 # Engine config
132 (('--engine-ip',), dict(
129 (('--engine-ip',), dict(
133 type=str, dest='FCEngineServiceFactory.ip', default=NoConfigDefault,
130 type=str, dest='FCEngineServiceFactory.ip', default=NoConfigDefault,
134 help='The IP address or hostname the controller will listen on for engine connections.',
131 help='The IP address or hostname the controller will listen on for engine connections.',
135 metavar='FCEngineServiceFactory.ip')
132 metavar='FCEngineServiceFactory.ip')
136 ),
133 ),
137 (('--engine-port',), dict(
134 (('--engine-port',), dict(
138 type=int, dest='FCEngineServiceFactory.port', default=NoConfigDefault,
135 type=int, dest='FCEngineServiceFactory.port', default=NoConfigDefault,
139 help='The port the controller will listen on for engine connections.',
136 help='The port the controller will listen on for engine connections.',
140 metavar='FCEngineServiceFactory.port')
137 metavar='FCEngineServiceFactory.port')
141 ),
138 ),
142 (('--engine-location',), dict(
139 (('--engine-location',), dict(
143 type=str, dest='FCEngineServiceFactory.location', default=NoConfigDefault,
140 type=str, dest='FCEngineServiceFactory.location', default=NoConfigDefault,
144 help='The hostname or ip that engines should connect to.',
141 help='The hostname or ip that engines should connect to.',
145 metavar='FCEngineServiceFactory.location')
142 metavar='FCEngineServiceFactory.location')
146 ),
143 ),
147 (('-y',), dict(
148 action='store_false', dest='FCEngineServiceFactory.secure', default=NoConfigDefault,
149 help='Turn off all engine security.')
150 ),
151 # Global config
144 # Global config
152 (('--log-to-file',), dict(
145 (('--log-to-file',), dict(
153 action='store_true', dest='Global.log_to_file', default=NoConfigDefault,
146 action='store_true', dest='Global.log_to_file', default=NoConfigDefault,
154 help='Log to a file in the log directory (default is stdout)')
147 help='Log to a file in the log directory (default is stdout)')
155 ),
148 ),
156 (('-r','--reuse-furls'), dict(
149 (('-r','--reuse-furls'), dict(
157 action='store_true', dest='Global.reuse_furls', default=NoConfigDefault,
150 action='store_true', dest='Global.reuse_furls', default=NoConfigDefault,
158 help='Try to reuse all FURL files.')
151 help='Try to reuse all FURL files.')
152 ),
153 (('-ns','--no-security'), dict(
154 action='store_false', dest='Global.secure', default=NoConfigDefault,
155 help='Turn off SSL encryption for all connections.')
159 )
156 )
160 )
157 )
161
158
162
159
163 class IPControllerAppCLConfigLoader(BaseAppArgParseConfigLoader):
160 class IPControllerAppCLConfigLoader(AppWithDirArgParseConfigLoader):
164
161
165 arguments = cl_args
162 arguments = cl_args
166
163
167
164
168 _default_config_file_name = 'ipcontroller_config.py'
165 default_config_file_name = 'ipcontroller_config.py'
169
166
170
167
171 class IPControllerApp(ApplicationWithDir):
168 class IPControllerApp(ApplicationWithDir):
172
169
173 name = 'ipcontroller'
170 name = 'ipcontroller'
171 app_dir_basename = 'cluster'
174 description = 'Start the IPython controller for parallel computing.'
172 description = 'Start the IPython controller for parallel computing.'
175 config_file_name = _default_config_file_name
173 config_file_name = default_config_file_name
176 default_log_level = logging.DEBUG
174 default_log_level = logging.WARN
177
175
178 def create_default_config(self):
176 def create_default_config(self):
179 super(IPControllerApp, self).create_default_config()
177 super(IPControllerApp, self).create_default_config()
180 self.default_config.Global.reuse_furls = False
178 self.default_config.Global.reuse_furls = False
179 self.default_config.Global.secure = True
181 self.default_config.Global.import_statements = []
180 self.default_config.Global.import_statements = []
182 self.default_config.Global.log_dir_name = 'log'
181 self.default_config.Global.log_dir_name = 'log'
183 self.default_config.Global.security_dir_name = 'security'
182 self.default_config.Global.security_dir_name = 'security'
184 self.default_config.Global.log_to_file = False
183 self.default_config.Global.log_to_file = False
185
184
185 def create_command_line_config(self):
186 """Create and return a command line config loader."""
187 return IPControllerAppCLConfigLoader(
188 description=self.description,
189 version=release.version
190 )
191
192 def post_load_command_line_config(self):
193 # Now setup reuse_furls
194 if hasattr(self.command_line_config.Global, 'reuse_furls'):
195 self.command_line_config.FCClientServiceFactory.reuse_furls = \
196 self.command_line_config.Global.reuse_furls
197 self.command_line_config.FCEngineServiceFactory.reuse_furls = \
198 self.command_line_config.Global.reuse_furls
199 del self.command_line_config.Global.reuse_furls
200 if hasattr(self.command_line_config.Global, 'secure'):
201 self.command_line_config.FCClientServiceFactory.secure = \
202 self.command_line_config.Global.secure
203 self.command_line_config.FCEngineServiceFactory.secure = \
204 self.command_line_config.Global.secure
205 del self.command_line_config.Global.secure
206
186 def pre_construct(self):
207 def pre_construct(self):
187 # Now set the security_dir and log_dir and create them. We use
208 # Now set the security_dir and log_dir and create them. We use
188 # the names an construct the absolute paths.
209 # the names an construct the absolute paths.
189 security_dir = os.path.join(self.master_config.Global.app_dir,
210 security_dir = os.path.join(self.master_config.Global.app_dir,
190 self.master_config.Global.security_dir_name)
211 self.master_config.Global.security_dir_name)
191 log_dir = os.path.join(self.master_config.Global.app_dir,
212 log_dir = os.path.join(self.master_config.Global.app_dir,
192 self.master_config.Global.log_dir_name)
213 self.master_config.Global.log_dir_name)
193 if not os.path.isdir(security_dir):
214 if not os.path.isdir(security_dir):
194 os.mkdir(security_dir, 0700)
215 os.mkdir(security_dir, 0700)
195 else:
216 else:
196 os.chmod(security_dir, 0700)
217 os.chmod(security_dir, 0700)
197 if not os.path.isdir(log_dir):
218 if not os.path.isdir(log_dir):
198 os.mkdir(log_dir, 0777)
219 os.mkdir(log_dir, 0777)
199
220
200 self.security_dir = self.master_config.Global.security_dir = security_dir
221 self.security_dir = self.master_config.Global.security_dir = security_dir
201 self.log_dir = self.master_config.Global.log_dir = log_dir
222 self.log_dir = self.master_config.Global.log_dir = log_dir
202
223
203 # Now setup reuse_furls
204 if hasattr(self.master_config.Global, 'reuse_furls'):
205 self.master_config.FCClientServiceFactory.reuse_furls = \
206 self.master_config.Global.reuse_furls
207 self.master_config.FCEngineServiceFactory.reuse_furls = \
208 self.master_config.Global.reuse_furls
209
210 def construct(self):
224 def construct(self):
211 # I am a little hesitant to put these into InteractiveShell itself.
225 # I am a little hesitant to put these into InteractiveShell itself.
212 # But that might be the place for them
226 # But that might be the place for them
213 sys.path.insert(0, '')
227 sys.path.insert(0, '')
214
228
215 self.start_logging()
229 self.start_logging()
216 self.import_statements()
230 self.import_statements()
217
231
218 # Create the service hierarchy
232 # Create the service hierarchy
219 self.main_service = service.MultiService()
233 self.main_service = service.MultiService()
220 # The controller service
234 # The controller service
221 controller_service = controllerservice.ControllerService()
235 controller_service = controllerservice.ControllerService()
222 controller_service.setServiceParent(self.main_service)
236 controller_service.setServiceParent(self.main_service)
223 # The client tub and all its refereceables
237 # The client tub and all its refereceables
224 csfactory = FCClientServiceFactory(self.master_config, controller_service)
238 csfactory = FCClientServiceFactory(self.master_config, controller_service)
225 client_service = csfactory.create()
239 client_service = csfactory.create()
226 client_service.setServiceParent(self.main_service)
240 client_service.setServiceParent(self.main_service)
227 # The engine tub
241 # The engine tub
228 esfactory = FCEngineServiceFactory(self.master_config, controller_service)
242 esfactory = FCEngineServiceFactory(self.master_config, controller_service)
229 engine_service = esfactory.create()
243 engine_service = esfactory.create()
230 engine_service.setServiceParent(self.main_service)
244 engine_service.setServiceParent(self.main_service)
231
245
232 def start_logging(self):
246 def start_logging(self):
233 if self.master_config.Global.log_to_file:
247 if self.master_config.Global.log_to_file:
234 log_filename = self.name + '-' + str(os.getpid()) + '.log'
248 log_filename = self.name + '-' + str(os.getpid()) + '.log'
235 logfile = os.path.join(self.log_dir, log_filename)
249 logfile = os.path.join(self.log_dir, log_filename)
236 open_log_file = open(logfile, 'w')
250 open_log_file = open(logfile, 'w')
237 else:
251 else:
238 open_log_file = sys.stdout
252 open_log_file = sys.stdout
239 log.startLogging(open_log_file)
253 log.startLogging(open_log_file)
240
254
241 def import_statements(self):
255 def import_statements(self):
242 statements = self.master_config.Global.import_statements
256 statements = self.master_config.Global.import_statements
243 for s in statements:
257 for s in statements:
244 try:
258 try:
259 log.msg("Executing statement: '%s'" % s)
245 exec s in globals(), locals()
260 exec s in globals(), locals()
246 except:
261 except:
247 log.msg("Error running import statement: %s" % s)
262 log.msg("Error running statement: %s" % s)
248
263
249 def start_app(self):
264 def start_app(self):
250 # Start the controller service and set things running
265 # Start the controller service and set things running
251 self.main_service.startService()
266 self.main_service.startService()
252 reactor.run()
267 reactor.run()
253
268
254
269
255 def launch_new_instance():
270 def launch_new_instance():
256 """Create and run the IPython controller"""
271 """Create and run the IPython controller"""
257 app = IPControllerApp()
272 app = IPControllerApp()
258 app.start()
273 app.start()
General Comments 0
You need to be logged in to leave comments. Login now