##// END OF EJS Templates
fix ipython-qtconsole when run as a GUI script
Min RK -
Show More
@@ -1,393 +1,398 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 A base class for a configurable application.
3 A base class for a configurable application.
4
4
5 Authors:
5 Authors:
6
6
7 * Brian Granger
7 * Brian Granger
8 * Min RK
8 * Min RK
9 """
9 """
10
10
11 #-----------------------------------------------------------------------------
11 #-----------------------------------------------------------------------------
12 # Copyright (C) 2008-2011 The IPython Development Team
12 # Copyright (C) 2008-2011 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 import logging
22 import logging
23 import os
23 import os
24 import re
24 import re
25 import sys
25 import sys
26 from copy import deepcopy
26 from copy import deepcopy
27
27
28 from IPython.config.configurable import SingletonConfigurable
28 from IPython.config.configurable import SingletonConfigurable
29 from IPython.config.loader import (
29 from IPython.config.loader import (
30 KeyValueConfigLoader, PyFileConfigLoader, Config, ArgumentError
30 KeyValueConfigLoader, PyFileConfigLoader, Config, ArgumentError
31 )
31 )
32
32
33 from IPython.utils.traitlets import (
33 from IPython.utils.traitlets import (
34 Unicode, List, Int, Enum, Dict, Instance, TraitError
34 Unicode, List, Int, Enum, Dict, Instance, TraitError
35 )
35 )
36 from IPython.utils.importstring import import_item
36 from IPython.utils.importstring import import_item
37 from IPython.utils.text import indent, wrap_paragraphs, dedent
37 from IPython.utils.text import indent, wrap_paragraphs, dedent
38
38
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40 # function for re-wrapping a helpstring
40 # function for re-wrapping a helpstring
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42
42
43 #-----------------------------------------------------------------------------
43 #-----------------------------------------------------------------------------
44 # Descriptions for the various sections
44 # Descriptions for the various sections
45 #-----------------------------------------------------------------------------
45 #-----------------------------------------------------------------------------
46
46
47 flag_description = """
47 flag_description = """
48 Flags are command-line arguments passed as '--<flag>'.
48 Flags are command-line arguments passed as '--<flag>'.
49 These take no parameters, unlike regular key-value arguments.
49 These take no parameters, unlike regular key-value arguments.
50 They are typically used for setting boolean flags, or enabling
50 They are typically used for setting boolean flags, or enabling
51 modes that involve setting multiple options together.
51 modes that involve setting multiple options together.
52
52
53 Flags *always* begin with '--', never just one '-'.
53 Flags *always* begin with '--', never just one '-'.
54 """.strip() # trim newlines of front and back
54 """.strip() # trim newlines of front and back
55
55
56 alias_description = """
56 alias_description = """
57 These are commonly set parameters, given abbreviated aliases for convenience.
57 These are commonly set parameters, given abbreviated aliases for convenience.
58 They are set in the same `name=value` way as class parameters, where
58 They are set in the same `name=value` way as class parameters, where
59 <name> is replaced by the real parameter for which it is an alias.
59 <name> is replaced by the real parameter for which it is an alias.
60 """.strip() # trim newlines of front and back
60 """.strip() # trim newlines of front and back
61
61
62 keyvalue_description = """
62 keyvalue_description = """
63 Parameters are set from command-line arguments of the form:
63 Parameters are set from command-line arguments of the form:
64 `Class.trait=value`. Parameters will *never* be prefixed with '-'.
64 `Class.trait=value`. Parameters will *never* be prefixed with '-'.
65 This line is evaluated in Python, so simple expressions are allowed, e.g.
65 This line is evaluated in Python, so simple expressions are allowed, e.g.
66 `C.a='range(3)'` For setting C.a=[0,1,2]
66 `C.a='range(3)'` For setting C.a=[0,1,2]
67 """.strip() # trim newlines of front and back
67 """.strip() # trim newlines of front and back
68
68
69 #-----------------------------------------------------------------------------
69 #-----------------------------------------------------------------------------
70 # Application class
70 # Application class
71 #-----------------------------------------------------------------------------
71 #-----------------------------------------------------------------------------
72
72
73
73
74 class ApplicationError(Exception):
74 class ApplicationError(Exception):
75 pass
75 pass
76
76
77
77
78 class Application(SingletonConfigurable):
78 class Application(SingletonConfigurable):
79 """A singleton application with full configuration support."""
79 """A singleton application with full configuration support."""
80
80
81 # The name of the application, will usually match the name of the command
81 # The name of the application, will usually match the name of the command
82 # line application
82 # line application
83 name = Unicode(u'application')
83 name = Unicode(u'application')
84
84
85 # The description of the application that is printed at the beginning
85 # The description of the application that is printed at the beginning
86 # of the help.
86 # of the help.
87 description = Unicode(u'This is an application.')
87 description = Unicode(u'This is an application.')
88 # default section descriptions
88 # default section descriptions
89 flag_description = Unicode(flag_description)
89 flag_description = Unicode(flag_description)
90 alias_description = Unicode(alias_description)
90 alias_description = Unicode(alias_description)
91 keyvalue_description = Unicode(keyvalue_description)
91 keyvalue_description = Unicode(keyvalue_description)
92
92
93
93
94 # A sequence of Configurable subclasses whose config=True attributes will
94 # A sequence of Configurable subclasses whose config=True attributes will
95 # be exposed at the command line.
95 # be exposed at the command line.
96 classes = List([])
96 classes = List([])
97
97
98 # The version string of this application.
98 # The version string of this application.
99 version = Unicode(u'0.0')
99 version = Unicode(u'0.0')
100
100
101 # The log level for the application
101 # The log level for the application
102 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
102 log_level = Enum((0,10,20,30,40,50,'DEBUG','INFO','WARN','ERROR','CRITICAL'),
103 default_value=logging.WARN,
103 default_value=logging.WARN,
104 config=True,
104 config=True,
105 help="Set the log level by value or name.")
105 help="Set the log level by value or name.")
106 def _log_level_changed(self, name, old, new):
106 def _log_level_changed(self, name, old, new):
107 """Adjust the log level when log_level is set."""
107 """Adjust the log level when log_level is set."""
108 if isinstance(new, basestring):
108 if isinstance(new, basestring):
109 new = getattr(logging, new)
109 new = getattr(logging, new)
110 self.log_level = new
110 self.log_level = new
111 self.log.setLevel(new)
111 self.log.setLevel(new)
112
112
113 # the alias map for configurables
113 # the alias map for configurables
114 aliases = Dict(dict(log_level='Application.log_level'))
114 aliases = Dict(dict(log_level='Application.log_level'))
115
115
116 # flags for loading Configurables or store_const style flags
116 # flags for loading Configurables or store_const style flags
117 # flags are loaded from this dict by '--key' flags
117 # flags are loaded from this dict by '--key' flags
118 # this must be a dict of two-tuples, the first element being the Config/dict
118 # this must be a dict of two-tuples, the first element being the Config/dict
119 # and the second being the help string for the flag
119 # and the second being the help string for the flag
120 flags = Dict()
120 flags = Dict()
121 def _flags_changed(self, name, old, new):
121 def _flags_changed(self, name, old, new):
122 """ensure flags dict is valid"""
122 """ensure flags dict is valid"""
123 for key,value in new.iteritems():
123 for key,value in new.iteritems():
124 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
124 assert len(value) == 2, "Bad flag: %r:%s"%(key,value)
125 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
125 assert isinstance(value[0], (dict, Config)), "Bad flag: %r:%s"%(key,value)
126 assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value)
126 assert isinstance(value[1], basestring), "Bad flag: %r:%s"%(key,value)
127
127
128
128
129 # subcommands for launching other applications
129 # subcommands for launching other applications
130 # if this is not empty, this will be a parent Application
130 # if this is not empty, this will be a parent Application
131 # this must be a dict of two-tuples,
131 # this must be a dict of two-tuples,
132 # the first element being the application class/import string
132 # the first element being the application class/import string
133 # and the second being the help string for the subcommand
133 # and the second being the help string for the subcommand
134 subcommands = Dict()
134 subcommands = Dict()
135 # parse_command_line will initialize a subapp, if requested
135 # parse_command_line will initialize a subapp, if requested
136 subapp = Instance('IPython.config.application.Application', allow_none=True)
136 subapp = Instance('IPython.config.application.Application', allow_none=True)
137
137
138 # extra command-line arguments that don't set config values
138 # extra command-line arguments that don't set config values
139 extra_args = List(Unicode)
139 extra_args = List(Unicode)
140
140
141
141
142 def __init__(self, **kwargs):
142 def __init__(self, **kwargs):
143 SingletonConfigurable.__init__(self, **kwargs)
143 SingletonConfigurable.__init__(self, **kwargs)
144 # Add my class to self.classes so my attributes appear in command line
144 # Add my class to self.classes so my attributes appear in command line
145 # options.
145 # options.
146 self.classes.insert(0, self.__class__)
146 self.classes.insert(0, self.__class__)
147
147
148 self.init_logging()
148 self.init_logging()
149
149
150 def _config_changed(self, name, old, new):
150 def _config_changed(self, name, old, new):
151 SingletonConfigurable._config_changed(self, name, old, new)
151 SingletonConfigurable._config_changed(self, name, old, new)
152 self.log.debug('Config changed:')
152 self.log.debug('Config changed:')
153 self.log.debug(repr(new))
153 self.log.debug(repr(new))
154
154
155 def init_logging(self):
155 def init_logging(self):
156 """Start logging for this application.
156 """Start logging for this application.
157
157
158 The default is to log to stdout using a StreaHandler. The log level
158 The default is to log to stdout using a StreaHandler. The log level
159 starts at loggin.WARN, but this can be adjusted by setting the
159 starts at loggin.WARN, but this can be adjusted by setting the
160 ``log_level`` attribute.
160 ``log_level`` attribute.
161 """
161 """
162 self.log = logging.getLogger(self.__class__.__name__)
162 self.log = logging.getLogger(self.__class__.__name__)
163 self.log.setLevel(self.log_level)
163 self.log.setLevel(self.log_level)
164 self._log_handler = logging.StreamHandler()
164 if sys.executable.endswith('pythonw.exe'):
165 # this should really go to a file, but file-logging is only
166 # hooked up in parallel applications
167 self._log_handler = logging.StreamHandler(open(os.devnull, 'w'))
168 else:
169 self._log_handler = logging.StreamHandler()
165 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
170 self._log_formatter = logging.Formatter("[%(name)s] %(message)s")
166 self._log_handler.setFormatter(self._log_formatter)
171 self._log_handler.setFormatter(self._log_formatter)
167 self.log.addHandler(self._log_handler)
172 self.log.addHandler(self._log_handler)
168
173
169 def initialize(self, argv=None):
174 def initialize(self, argv=None):
170 """Do the basic steps to configure me.
175 """Do the basic steps to configure me.
171
176
172 Override in subclasses.
177 Override in subclasses.
173 """
178 """
174 self.parse_command_line(argv)
179 self.parse_command_line(argv)
175
180
176
181
177 def start(self):
182 def start(self):
178 """Start the app mainloop.
183 """Start the app mainloop.
179
184
180 Override in subclasses.
185 Override in subclasses.
181 """
186 """
182 if self.subapp is not None:
187 if self.subapp is not None:
183 return self.subapp.start()
188 return self.subapp.start()
184
189
185 def print_alias_help(self):
190 def print_alias_help(self):
186 """Print the alias part of the help."""
191 """Print the alias part of the help."""
187 if not self.aliases:
192 if not self.aliases:
188 return
193 return
189
194
190 lines = ['Aliases']
195 lines = ['Aliases']
191 lines.append('-'*len(lines[0]))
196 lines.append('-'*len(lines[0]))
192 lines.append('')
197 lines.append('')
193 for p in wrap_paragraphs(self.alias_description):
198 for p in wrap_paragraphs(self.alias_description):
194 lines.append(p)
199 lines.append(p)
195 lines.append('')
200 lines.append('')
196
201
197 classdict = {}
202 classdict = {}
198 for cls in self.classes:
203 for cls in self.classes:
199 # include all parents (up to, but excluding Configurable) in available names
204 # include all parents (up to, but excluding Configurable) in available names
200 for c in cls.mro()[:-3]:
205 for c in cls.mro()[:-3]:
201 classdict[c.__name__] = c
206 classdict[c.__name__] = c
202
207
203 for alias, longname in self.aliases.iteritems():
208 for alias, longname in self.aliases.iteritems():
204 classname, traitname = longname.split('.',1)
209 classname, traitname = longname.split('.',1)
205 cls = classdict[classname]
210 cls = classdict[classname]
206
211
207 trait = cls.class_traits(config=True)[traitname]
212 trait = cls.class_traits(config=True)[traitname]
208 help = cls.class_get_trait_help(trait)
213 help = cls.class_get_trait_help(trait)
209 help = help.replace(longname, "%s (%s)"%(alias, longname), 1)
214 help = help.replace(longname, "%s (%s)"%(alias, longname), 1)
210 lines.append(help)
215 lines.append(help)
211 lines.append('')
216 lines.append('')
212 print '\n'.join(lines)
217 print '\n'.join(lines)
213
218
214 def print_flag_help(self):
219 def print_flag_help(self):
215 """Print the flag part of the help."""
220 """Print the flag part of the help."""
216 if not self.flags:
221 if not self.flags:
217 return
222 return
218
223
219 lines = ['Flags']
224 lines = ['Flags']
220 lines.append('-'*len(lines[0]))
225 lines.append('-'*len(lines[0]))
221 lines.append('')
226 lines.append('')
222 for p in wrap_paragraphs(self.flag_description):
227 for p in wrap_paragraphs(self.flag_description):
223 lines.append(p)
228 lines.append(p)
224 lines.append('')
229 lines.append('')
225
230
226 for m, (cfg,help) in self.flags.iteritems():
231 for m, (cfg,help) in self.flags.iteritems():
227 lines.append('--'+m)
232 lines.append('--'+m)
228 lines.append(indent(dedent(help.strip())))
233 lines.append(indent(dedent(help.strip())))
229 lines.append('')
234 lines.append('')
230 print '\n'.join(lines)
235 print '\n'.join(lines)
231
236
232 def print_subcommands(self):
237 def print_subcommands(self):
233 """Print the subcommand part of the help."""
238 """Print the subcommand part of the help."""
234 if not self.subcommands:
239 if not self.subcommands:
235 return
240 return
236
241
237 lines = ["Subcommands"]
242 lines = ["Subcommands"]
238 lines.append('-'*len(lines[0]))
243 lines.append('-'*len(lines[0]))
239 for subc, (cls,help) in self.subcommands.iteritems():
244 for subc, (cls,help) in self.subcommands.iteritems():
240 lines.append("%s : %s"%(subc, cls))
245 lines.append("%s : %s"%(subc, cls))
241 if help:
246 if help:
242 lines.append(indent(dedent(help.strip())))
247 lines.append(indent(dedent(help.strip())))
243 lines.append('')
248 lines.append('')
244 print '\n'.join(lines)
249 print '\n'.join(lines)
245
250
246 def print_help(self, classes=False):
251 def print_help(self, classes=False):
247 """Print the help for each Configurable class in self.classes.
252 """Print the help for each Configurable class in self.classes.
248
253
249 If classes=False (the default), only flags and aliases are printed.
254 If classes=False (the default), only flags and aliases are printed.
250 """
255 """
251 self.print_subcommands()
256 self.print_subcommands()
252 self.print_flag_help()
257 self.print_flag_help()
253 self.print_alias_help()
258 self.print_alias_help()
254
259
255 if classes:
260 if classes:
256 if self.classes:
261 if self.classes:
257 print "Class parameters"
262 print "Class parameters"
258 print "----------------"
263 print "----------------"
259 print
264 print
260 for p in wrap_paragraphs(self.keyvalue_description):
265 for p in wrap_paragraphs(self.keyvalue_description):
261 print p
266 print p
262 print
267 print
263
268
264 for cls in self.classes:
269 for cls in self.classes:
265 cls.class_print_help()
270 cls.class_print_help()
266 print
271 print
267 else:
272 else:
268 print "To see all available configurables, use `--help-all`"
273 print "To see all available configurables, use `--help-all`"
269 print
274 print
270
275
271 def print_description(self):
276 def print_description(self):
272 """Print the application description."""
277 """Print the application description."""
273 for p in wrap_paragraphs(self.description):
278 for p in wrap_paragraphs(self.description):
274 print p
279 print p
275 print
280 print
276
281
277 def print_version(self):
282 def print_version(self):
278 """Print the version string."""
283 """Print the version string."""
279 print self.version
284 print self.version
280
285
281 def update_config(self, config):
286 def update_config(self, config):
282 """Fire the traits events when the config is updated."""
287 """Fire the traits events when the config is updated."""
283 # Save a copy of the current config.
288 # Save a copy of the current config.
284 newconfig = deepcopy(self.config)
289 newconfig = deepcopy(self.config)
285 # Merge the new config into the current one.
290 # Merge the new config into the current one.
286 newconfig._merge(config)
291 newconfig._merge(config)
287 # Save the combined config as self.config, which triggers the traits
292 # Save the combined config as self.config, which triggers the traits
288 # events.
293 # events.
289 self.config = newconfig
294 self.config = newconfig
290
295
291 def initialize_subcommand(self, subc, argv=None):
296 def initialize_subcommand(self, subc, argv=None):
292 """Initialize a subcommand with argv."""
297 """Initialize a subcommand with argv."""
293 subapp,help = self.subcommands.get(subc)
298 subapp,help = self.subcommands.get(subc)
294
299
295 if isinstance(subapp, basestring):
300 if isinstance(subapp, basestring):
296 subapp = import_item(subapp)
301 subapp = import_item(subapp)
297
302
298 # clear existing instances
303 # clear existing instances
299 self.__class__.clear_instance()
304 self.__class__.clear_instance()
300 # instantiate
305 # instantiate
301 self.subapp = subapp.instance()
306 self.subapp = subapp.instance()
302 # and initialize subapp
307 # and initialize subapp
303 self.subapp.initialize(argv)
308 self.subapp.initialize(argv)
304
309
305 def parse_command_line(self, argv=None):
310 def parse_command_line(self, argv=None):
306 """Parse the command line arguments."""
311 """Parse the command line arguments."""
307 argv = sys.argv[1:] if argv is None else argv
312 argv = sys.argv[1:] if argv is None else argv
308
313
309 if self.subcommands and len(argv) > 0:
314 if self.subcommands and len(argv) > 0:
310 # we have subcommands, and one may have been specified
315 # we have subcommands, and one may have been specified
311 subc, subargv = argv[0], argv[1:]
316 subc, subargv = argv[0], argv[1:]
312 if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
317 if re.match(r'^\w(\-?\w)*$', subc) and subc in self.subcommands:
313 # it's a subcommand, and *not* a flag or class parameter
318 # it's a subcommand, and *not* a flag or class parameter
314 return self.initialize_subcommand(subc, subargv)
319 return self.initialize_subcommand(subc, subargv)
315
320
316 if '-h' in argv or '--help' in argv or '--help-all' in argv:
321 if '-h' in argv or '--help' in argv or '--help-all' in argv:
317 self.print_description()
322 self.print_description()
318 self.print_help('--help-all' in argv)
323 self.print_help('--help-all' in argv)
319 self.exit(0)
324 self.exit(0)
320
325
321 if '--version' in argv:
326 if '--version' in argv:
322 self.print_version()
327 self.print_version()
323 self.exit(0)
328 self.exit(0)
324
329
325 loader = KeyValueConfigLoader(argv=argv, aliases=self.aliases,
330 loader = KeyValueConfigLoader(argv=argv, aliases=self.aliases,
326 flags=self.flags)
331 flags=self.flags)
327 try:
332 try:
328 config = loader.load_config()
333 config = loader.load_config()
329 self.update_config(config)
334 self.update_config(config)
330 except (TraitError, ArgumentError) as e:
335 except (TraitError, ArgumentError) as e:
331 self.print_description()
336 self.print_description()
332 self.print_help()
337 self.print_help()
333 self.log.fatal(str(e))
338 self.log.fatal(str(e))
334 self.exit(1)
339 self.exit(1)
335 # store unparsed args in extra_args
340 # store unparsed args in extra_args
336 self.extra_args = loader.extra_args
341 self.extra_args = loader.extra_args
337
342
338 def load_config_file(self, filename, path=None):
343 def load_config_file(self, filename, path=None):
339 """Load a .py based config file by filename and path."""
344 """Load a .py based config file by filename and path."""
340 loader = PyFileConfigLoader(filename, path=path)
345 loader = PyFileConfigLoader(filename, path=path)
341 config = loader.load_config()
346 config = loader.load_config()
342 self.update_config(config)
347 self.update_config(config)
343
348
344 def generate_config_file(self):
349 def generate_config_file(self):
345 """generate default config file from Configurables"""
350 """generate default config file from Configurables"""
346 lines = ["# Configuration file for %s."%self.name]
351 lines = ["# Configuration file for %s."%self.name]
347 lines.append('')
352 lines.append('')
348 lines.append('c = get_config()')
353 lines.append('c = get_config()')
349 lines.append('')
354 lines.append('')
350 for cls in self.classes:
355 for cls in self.classes:
351 lines.append(cls.class_config_section())
356 lines.append(cls.class_config_section())
352 return '\n'.join(lines)
357 return '\n'.join(lines)
353
358
354 def exit(self, exit_status=0):
359 def exit(self, exit_status=0):
355 self.log.debug("Exiting application: %s" % self.name)
360 self.log.debug("Exiting application: %s" % self.name)
356 sys.exit(exit_status)
361 sys.exit(exit_status)
357
362
358 #-----------------------------------------------------------------------------
363 #-----------------------------------------------------------------------------
359 # utility functions, for convenience
364 # utility functions, for convenience
360 #-----------------------------------------------------------------------------
365 #-----------------------------------------------------------------------------
361
366
362 def boolean_flag(name, configurable, set_help='', unset_help=''):
367 def boolean_flag(name, configurable, set_help='', unset_help=''):
363 """Helper for building basic --trait, --no-trait flags.
368 """Helper for building basic --trait, --no-trait flags.
364
369
365 Parameters
370 Parameters
366 ----------
371 ----------
367
372
368 name : str
373 name : str
369 The name of the flag.
374 The name of the flag.
370 configurable : str
375 configurable : str
371 The 'Class.trait' string of the trait to be set/unset with the flag
376 The 'Class.trait' string of the trait to be set/unset with the flag
372 set_help : unicode
377 set_help : unicode
373 help string for --name flag
378 help string for --name flag
374 unset_help : unicode
379 unset_help : unicode
375 help string for --no-name flag
380 help string for --no-name flag
376
381
377 Returns
382 Returns
378 -------
383 -------
379
384
380 cfg : dict
385 cfg : dict
381 A dict with two keys: 'name', and 'no-name', for setting and unsetting
386 A dict with two keys: 'name', and 'no-name', for setting and unsetting
382 the trait, respectively.
387 the trait, respectively.
383 """
388 """
384 # default helpstrings
389 # default helpstrings
385 set_help = set_help or "set %s=True"%configurable
390 set_help = set_help or "set %s=True"%configurable
386 unset_help = unset_help or "set %s=False"%configurable
391 unset_help = unset_help or "set %s=False"%configurable
387
392
388 cls,trait = configurable.split('.')
393 cls,trait = configurable.split('.')
389
394
390 setter = {cls : {trait : True}}
395 setter = {cls : {trait : True}}
391 unsetter = {cls : {trait : False}}
396 unsetter = {cls : {trait : False}}
392 return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
397 return {name : (setter, set_help), 'no-'+name : (unsetter, unset_help)}
393
398
@@ -1,216 +1,216 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 """An Application for launching a kernel
2 """An Application for launching a kernel
3
3
4 Authors
4 Authors
5 -------
5 -------
6 * MinRK
6 * MinRK
7 """
7 """
8 #-----------------------------------------------------------------------------
8 #-----------------------------------------------------------------------------
9 # Copyright (C) 2011 The IPython Development Team
9 # Copyright (C) 2011 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.txt, distributed as part of this software.
12 # the file COPYING.txt, distributed as part of this software.
13 #-----------------------------------------------------------------------------
13 #-----------------------------------------------------------------------------
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Imports
16 # Imports
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18
18
19 # Standard library imports.
19 # Standard library imports.
20 import os
20 import os
21 import sys
21 import sys
22
22
23 # System library imports.
23 # System library imports.
24 import zmq
24 import zmq
25
25
26 # IPython imports.
26 # IPython imports.
27 from IPython.core.ultratb import FormattedTB
27 from IPython.core.ultratb import FormattedTB
28 from IPython.core.application import (
28 from IPython.core.application import (
29 BaseIPythonApplication, base_flags, base_aliases
29 BaseIPythonApplication, base_flags, base_aliases
30 )
30 )
31 from IPython.utils import io
31 from IPython.utils import io
32 from IPython.utils.localinterfaces import LOCALHOST
32 from IPython.utils.localinterfaces import LOCALHOST
33 from IPython.utils.traitlets import (Any, Instance, Dict, Unicode, Int, Bool,
33 from IPython.utils.traitlets import (Any, Instance, Dict, Unicode, Int, Bool,
34 DottedObjectName)
34 DottedObjectName)
35 from IPython.utils.importstring import import_item
35 from IPython.utils.importstring import import_item
36 # local imports
36 # local imports
37 from IPython.zmq.heartbeat import Heartbeat
37 from IPython.zmq.heartbeat import Heartbeat
38 from IPython.zmq.parentpoller import ParentPollerUnix, ParentPollerWindows
38 from IPython.zmq.parentpoller import ParentPollerUnix, ParentPollerWindows
39 from IPython.zmq.session import Session
39 from IPython.zmq.session import Session
40
40
41
41
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43 # Flags and Aliases
43 # Flags and Aliases
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45
45
46 kernel_aliases = dict(base_aliases)
46 kernel_aliases = dict(base_aliases)
47 kernel_aliases.update({
47 kernel_aliases.update({
48 'ip' : 'KernelApp.ip',
48 'ip' : 'KernelApp.ip',
49 'hb' : 'KernelApp.hb_port',
49 'hb' : 'KernelApp.hb_port',
50 'shell' : 'KernelApp.shell_port',
50 'shell' : 'KernelApp.shell_port',
51 'iopub' : 'KernelApp.iopub_port',
51 'iopub' : 'KernelApp.iopub_port',
52 'stdin' : 'KernelApp.stdin_port',
52 'stdin' : 'KernelApp.stdin_port',
53 'parent': 'KernelApp.parent',
53 'parent': 'KernelApp.parent',
54 })
54 })
55 if sys.platform.startswith('win'):
55 if sys.platform.startswith('win'):
56 kernel_aliases['interrupt'] = 'KernelApp.interrupt'
56 kernel_aliases['interrupt'] = 'KernelApp.interrupt'
57
57
58 kernel_flags = dict(base_flags)
58 kernel_flags = dict(base_flags)
59 kernel_flags.update({
59 kernel_flags.update({
60 'no-stdout' : (
60 'no-stdout' : (
61 {'KernelApp' : {'no_stdout' : True}},
61 {'KernelApp' : {'no_stdout' : True}},
62 "redirect stdout to the null device"),
62 "redirect stdout to the null device"),
63 'no-stderr' : (
63 'no-stderr' : (
64 {'KernelApp' : {'no_stderr' : True}},
64 {'KernelApp' : {'no_stderr' : True}},
65 "redirect stderr to the null device"),
65 "redirect stderr to the null device"),
66 })
66 })
67
67
68
68
69 #-----------------------------------------------------------------------------
69 #-----------------------------------------------------------------------------
70 # Application class for starting a Kernel
70 # Application class for starting a Kernel
71 #-----------------------------------------------------------------------------
71 #-----------------------------------------------------------------------------
72
72
73 class KernelApp(BaseIPythonApplication):
73 class KernelApp(BaseIPythonApplication):
74 name='pykernel'
74 name='pykernel'
75 aliases = Dict(kernel_aliases)
75 aliases = Dict(kernel_aliases)
76 flags = Dict(kernel_flags)
76 flags = Dict(kernel_flags)
77 classes = [Session]
77 classes = [Session]
78 # the kernel class, as an importstring
78 # the kernel class, as an importstring
79 kernel_class = DottedObjectName('IPython.zmq.pykernel.Kernel')
79 kernel_class = DottedObjectName('IPython.zmq.pykernel.Kernel')
80 kernel = Any()
80 kernel = Any()
81 poller = Any() # don't restrict this even though current pollers are all Threads
81 poller = Any() # don't restrict this even though current pollers are all Threads
82 heartbeat = Instance(Heartbeat)
82 heartbeat = Instance(Heartbeat)
83 session = Instance('IPython.zmq.session.Session')
83 session = Instance('IPython.zmq.session.Session')
84 ports = Dict()
84 ports = Dict()
85
85
86 # connection info:
86 # connection info:
87 ip = Unicode(LOCALHOST, config=True,
87 ip = Unicode(LOCALHOST, config=True,
88 help="Set the IP or interface on which the kernel will listen.")
88 help="Set the IP or interface on which the kernel will listen.")
89 hb_port = Int(0, config=True, help="set the heartbeat port [default: random]")
89 hb_port = Int(0, config=True, help="set the heartbeat port [default: random]")
90 shell_port = Int(0, config=True, help="set the shell (XREP) port [default: random]")
90 shell_port = Int(0, config=True, help="set the shell (XREP) port [default: random]")
91 iopub_port = Int(0, config=True, help="set the iopub (PUB) port [default: random]")
91 iopub_port = Int(0, config=True, help="set the iopub (PUB) port [default: random]")
92 stdin_port = Int(0, config=True, help="set the stdin (XREQ) port [default: random]")
92 stdin_port = Int(0, config=True, help="set the stdin (XREQ) port [default: random]")
93
93
94 # streams, etc.
94 # streams, etc.
95 no_stdout = Bool(False, config=True, help="redirect stdout to the null device")
95 no_stdout = Bool(False, config=True, help="redirect stdout to the null device")
96 no_stderr = Bool(False, config=True, help="redirect stderr to the null device")
96 no_stderr = Bool(False, config=True, help="redirect stderr to the null device")
97 outstream_class = DottedObjectName('IPython.zmq.iostream.OutStream',
97 outstream_class = DottedObjectName('IPython.zmq.iostream.OutStream',
98 config=True, help="The importstring for the OutStream factory")
98 config=True, help="The importstring for the OutStream factory")
99 displayhook_class = DottedObjectName('IPython.zmq.displayhook.ZMQDisplayHook',
99 displayhook_class = DottedObjectName('IPython.zmq.displayhook.ZMQDisplayHook',
100 config=True, help="The importstring for the DisplayHook factory")
100 config=True, help="The importstring for the DisplayHook factory")
101
101
102 # polling
102 # polling
103 parent = Int(0, config=True,
103 parent = Int(0, config=True,
104 help="""kill this process if its parent dies. On Windows, the argument
104 help="""kill this process if its parent dies. On Windows, the argument
105 specifies the HANDLE of the parent process, otherwise it is simply boolean.
105 specifies the HANDLE of the parent process, otherwise it is simply boolean.
106 """)
106 """)
107 interrupt = Int(0, config=True,
107 interrupt = Int(0, config=True,
108 help="""ONLY USED ON WINDOWS
108 help="""ONLY USED ON WINDOWS
109 Interrupt this process when the parent is signalled.
109 Interrupt this process when the parent is signalled.
110 """)
110 """)
111
111
112 def init_crash_handler(self):
112 def init_crash_handler(self):
113 # Install minimal exception handling
113 # Install minimal exception handling
114 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
114 sys.excepthook = FormattedTB(mode='Verbose', color_scheme='NoColor',
115 ostream=sys.__stdout__)
115 ostream=sys.__stdout__)
116
116
117 def init_poller(self):
117 def init_poller(self):
118 if sys.platform == 'win32':
118 if sys.platform == 'win32':
119 if self.interrupt or self.parent:
119 if self.interrupt or self.parent:
120 self.poller = ParentPollerWindows(self.interrupt, self.parent)
120 self.poller = ParentPollerWindows(self.interrupt, self.parent)
121 elif self.parent:
121 elif self.parent:
122 self.poller = ParentPollerUnix()
122 self.poller = ParentPollerUnix()
123
123
124 def _bind_socket(self, s, port):
124 def _bind_socket(self, s, port):
125 iface = 'tcp://%s' % self.ip
125 iface = 'tcp://%s' % self.ip
126 if port <= 0:
126 if port <= 0:
127 port = s.bind_to_random_port(iface)
127 port = s.bind_to_random_port(iface)
128 else:
128 else:
129 s.bind(iface + ':%i'%port)
129 s.bind(iface + ':%i'%port)
130 return port
130 return port
131
131
132 def init_sockets(self):
132 def init_sockets(self):
133 # Create a context, a session, and the kernel sockets.
133 # Create a context, a session, and the kernel sockets.
134 io.raw_print("Starting the kernel at pid:", os.getpid())
134 io.raw_print("Starting the kernel at pid:", os.getpid())
135 context = zmq.Context.instance()
135 context = zmq.Context.instance()
136 # Uncomment this to try closing the context.
136 # Uncomment this to try closing the context.
137 # atexit.register(context.term)
137 # atexit.register(context.term)
138
138
139 self.shell_socket = context.socket(zmq.XREP)
139 self.shell_socket = context.socket(zmq.XREP)
140 self.shell_port = self._bind_socket(self.shell_socket, self.shell_port)
140 self.shell_port = self._bind_socket(self.shell_socket, self.shell_port)
141 self.log.debug("shell XREP Channel on port: %i"%self.shell_port)
141 self.log.debug("shell XREP Channel on port: %i"%self.shell_port)
142
142
143 self.iopub_socket = context.socket(zmq.PUB)
143 self.iopub_socket = context.socket(zmq.PUB)
144 self.iopub_port = self._bind_socket(self.iopub_socket, self.iopub_port)
144 self.iopub_port = self._bind_socket(self.iopub_socket, self.iopub_port)
145 self.log.debug("iopub PUB Channel on port: %i"%self.iopub_port)
145 self.log.debug("iopub PUB Channel on port: %i"%self.iopub_port)
146
146
147 self.stdin_socket = context.socket(zmq.XREQ)
147 self.stdin_socket = context.socket(zmq.XREQ)
148 self.stdin_port = self._bind_socket(self.stdin_socket, self.stdin_port)
148 self.stdin_port = self._bind_socket(self.stdin_socket, self.stdin_port)
149 self.log.debug("stdin XREQ Channel on port: %i"%self.stdin_port)
149 self.log.debug("stdin XREQ Channel on port: %i"%self.stdin_port)
150
150
151 self.heartbeat = Heartbeat(context, (self.ip, self.hb_port))
151 self.heartbeat = Heartbeat(context, (self.ip, self.hb_port))
152 self.hb_port = self.heartbeat.port
152 self.hb_port = self.heartbeat.port
153 self.log.debug("Heartbeat REP Channel on port: %i"%self.hb_port)
153 self.log.debug("Heartbeat REP Channel on port: %i"%self.hb_port)
154
154
155 # Helper to make it easier to connect to an existing kernel, until we have
155 # Helper to make it easier to connect to an existing kernel, until we have
156 # single-port connection negotiation fully implemented.
156 # single-port connection negotiation fully implemented.
157 self.log.info("To connect another client to this kernel, use:")
157 self.log.info("To connect another client to this kernel, use:")
158 self.log.info("--external shell={0} iopub={1} stdin={2} hb={3}".format(
158 self.log.info("--external shell={0} iopub={1} stdin={2} hb={3}".format(
159 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port))
159 self.shell_port, self.iopub_port, self.stdin_port, self.hb_port))
160
160
161
161
162 self.ports = dict(shell=self.shell_port, iopub=self.iopub_port,
162 self.ports = dict(shell=self.shell_port, iopub=self.iopub_port,
163 stdin=self.stdin_port, hb=self.hb_port)
163 stdin=self.stdin_port, hb=self.hb_port)
164
164
165 def init_session(self):
165 def init_session(self):
166 """create our session object"""
166 """create our session object"""
167 self.session = Session(config=self.config, username=u'kernel')
167 self.session = Session(config=self.config, username=u'kernel')
168
168
169 def init_io(self):
169 def init_blackhole(self):
170 """redirects stdout/stderr, and installs a display hook"""
170 """redirects stdout/stderr to devnull if necessary"""
171 # Re-direct stdout/stderr, if necessary.
172 if self.no_stdout or self.no_stderr:
171 if self.no_stdout or self.no_stderr:
173 blackhole = file(os.devnull, 'w')
172 blackhole = file(os.devnull, 'w')
174 if self.no_stdout:
173 if self.no_stdout:
175 sys.stdout = sys.__stdout__ = blackhole
174 sys.stdout = sys.__stdout__ = blackhole
176 if self.no_stderr:
175 if self.no_stderr:
177 sys.stderr = sys.__stderr__ = blackhole
176 sys.stderr = sys.__stderr__ = blackhole
178
177
179 # Redirect input streams and set a display hook.
178 def init_io(self):
180
179 """Redirect input streams and set a display hook."""
181 if self.outstream_class:
180 if self.outstream_class:
182 outstream_factory = import_item(str(self.outstream_class))
181 outstream_factory = import_item(str(self.outstream_class))
183 sys.stdout = outstream_factory(self.session, self.iopub_socket, u'stdout')
182 sys.stdout = outstream_factory(self.session, self.iopub_socket, u'stdout')
184 sys.stderr = outstream_factory(self.session, self.iopub_socket, u'stderr')
183 sys.stderr = outstream_factory(self.session, self.iopub_socket, u'stderr')
185 if self.displayhook_class:
184 if self.displayhook_class:
186 displayhook_factory = import_item(str(self.displayhook_class))
185 displayhook_factory = import_item(str(self.displayhook_class))
187 sys.displayhook = displayhook_factory(self.session, self.iopub_socket)
186 sys.displayhook = displayhook_factory(self.session, self.iopub_socket)
188
187
189 def init_kernel(self):
188 def init_kernel(self):
190 """Create the Kernel object itself"""
189 """Create the Kernel object itself"""
191 kernel_factory = import_item(str(self.kernel_class))
190 kernel_factory = import_item(str(self.kernel_class))
192 self.kernel = kernel_factory(config=self.config, session=self.session,
191 self.kernel = kernel_factory(config=self.config, session=self.session,
193 shell_socket=self.shell_socket,
192 shell_socket=self.shell_socket,
194 iopub_socket=self.iopub_socket,
193 iopub_socket=self.iopub_socket,
195 stdin_socket=self.stdin_socket,
194 stdin_socket=self.stdin_socket,
196 log=self.log
195 log=self.log
197 )
196 )
198 self.kernel.record_ports(self.ports)
197 self.kernel.record_ports(self.ports)
199
198
200 def initialize(self, argv=None):
199 def initialize(self, argv=None):
201 super(KernelApp, self).initialize(argv)
200 super(KernelApp, self).initialize(argv)
201 self.init_blackhole()
202 self.init_session()
202 self.init_session()
203 self.init_poller()
203 self.init_poller()
204 self.init_sockets()
204 self.init_sockets()
205 self.init_io()
205 self.init_io()
206 self.init_kernel()
206 self.init_kernel()
207
207
208 def start(self):
208 def start(self):
209 self.heartbeat.start()
209 self.heartbeat.start()
210 if self.poller is not None:
210 if self.poller is not None:
211 self.poller.start()
211 self.poller.start()
212 try:
212 try:
213 self.kernel.start()
213 self.kernel.start()
214 except KeyboardInterrupt:
214 except KeyboardInterrupt:
215 pass
215 pass
216
216
General Comments 0
You need to be logged in to leave comments. Login now