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